コンピュータサイエンスにおいて、ミューテーターメソッドとは、変数の変更を制御するために使用されるメソッドです。これらはセッターメソッドとも呼ばれます。多くの場合、セッターにはゲッターが伴い、ゲッターはプライベートメンバ変数の値を返します。これらはまとめてアクセサーとも呼ばれます。
ミューテーターメソッドは、カプセル化の原則に従って、オブジェクト指向プログラミングで最もよく使用されます。この原則によれば、クラスのメンバ変数は、他のコードから隠蔽および保護するためにプライベートに設定され、パブリックメンバ関数(ミューテーターメソッド)によってのみ変更できます。このパブリックメンバ関数は、目的の新しい値をパラメータとして受け取り、必要に応じて検証を行い、プライベートメンバ変数を変更します。ミューテーターメソッドは、代入演算子のオーバーロードと比較できますが、通常はオブジェクト階層の異なるレベルで使用されます。
ミューテーターメソッドは、オブジェクト指向ではない環境でも使用できます。この場合、変更対象の変数への参照と新しい値がミューテーターに渡されます。このシナリオでは、コンパイラはコードがミューテーターメソッドを迂回して変数を直接変更することを制限できません。変数がミューテーターメソッドを介してのみ変更され、直接変更されないようにするのは開発者の責任です。
プロパティをサポートするプログラミング言語では、カプセル化の利便性を損なうことなく、プロパティは便利な代替手段を提供する。
以下の例では、完全に実装されたミューテーターメソッドは、入力データを検証したり、イベントをトリガーするなどのさらなるアクションを実行したりすることもできます。
ミューテーターメソッドとアクセサーメソッド、またはプロパティブロックを定義する代わりに、インスタンス変数にプライベート以外の可視性を与え、オブジェクトの外部から直接アクセスする方法があります。ミューテーターとアクセサーを使用すると、アクセス権限をより細かく制御できます。たとえば、アクセサーを定義するだけで、パラメータを読み取り専用にすることができます。ただし、ミューテーターは定義しません。2つのメソッドの可視性は異なる場合があり、多くの場合、アクセサーをパブリックにし、ミューテーターを保護、パッケージプライベート、または内部にしておくと便利です。
ミューテーターが定義されているブロックは、受信データの検証または前処理を行う機会を提供します。すべての外部アクセスがミューテーターを経由することが保証されている場合、これらの手順を省略することはできません。たとえば、日付が個別のプライベート変数year、monthおよびで表されている場合day、受信日付はミューテーターによって分割され、setDate一貫性を保つために同じプライベートインスタンス変数がおよびによってアクセスされますsetYear。setMonthいずれの場合も、1~12以外の月の値は同じコードによって拒否されます。
一方、アクセサは、内部変数から有用なデータ表現を合成することを可能にすると同時に、その構造をカプセル化して外部モジュールから隠蔽します。例えば、通貨getAmountアクセサは、隠されたパラメータで定義された小数点以下の桁数を持つ数値変数から文字列を構築できますcurrency。
現代のプログラミング言語では、C#や Ruby のように、ミューテーターやアクセサーの定型コードを1 行で生成できる機能がよく提供されています。このような場合、検証、前処理、合成のためのコード ブロックは作成されません。これらの簡略化されたアクセサーは、単純なパブリック インスタンス変数よりもカプセル化の利点を維持していますが、システム設計が進み、ソフトウェアが保守され、要件が変化するにつれて、データに対する要求がより高度になることがよくあります。多くの自動ミューテーターとアクセサーは、最終的に個別のコード ブロックに置き換えられます。実装の初期段階でこれらを自動的に作成する利点は、より高度な機能が追加されるかどうかに関わらず、クラスのパブリック インターフェイスが同一のままであり、追加された場合でも大規模なリファクタリングが不要になることです。[ 1 ]public string Name { get; set; }attr_accessor :name
ミューテーターやアクセサーを持つパラメータを、それらが定義されているクラス内部から操作する場合、多くの場合、追加の検討が必要となります。実装の初期段階では、これらのブロックに追加のコードがほとんど、あるいは全くないため、プライベートインスタンス変数に直接アクセスしても、そうでなくても違いはありません。しかし、検証、相互検証、データ整合性チェック、前処理、その他の高度な処理が追加されるにつれて、内部アクセスの一部で新しいコードが使用される一方で、他の部分ではそれが回避されるといった、微妙なバグが発生する可能性があります。
アクセサ関数は、追加の手順が必要となるため、データフィールドを直接取得または保存するよりも効率が悪くなる可能性がありますが、[ 2 ]そのような関数は多くの場合インライン化されるため、関数呼び出しのオーバーヘッドがなくなります。
![]()
学生構造体年齢dd ?学生終了.code student_get_age proc object : DWORD mov ebx , object mov eax , student.age [ ebx ] ret student_get_age endpstudent_set_age proc object : DWORD , age : DWORD mov ebx , object mov eax , age mov student.age [ ebx ], eax ret student_set_age endpファイルStudent.h内:
#pragma oncestruct Student ; /* 不透明な構造体 */ typedef struct Student Student ;Student * createStudent ( int age , const char * name ); void destroyStudent ( const Student * s );void setStudentAge ( Student * s , int age ); int getStudentAge ( const Student * s ); char * getStudentName ( const Student * s );ファイルStudent.c内:
#include <stdlib.h> #include <string.h>#include "Student.h"struct Student { int age ; char * name ; };Student * createStudent ( int age , const char * name ) { Student * s = ( Student * ) malloc ( sizeof ( Student )); s -> name = strdup ( name ); s -> age = age ; return s ; }void destroyStudent ( const Student * s ) { free ( s- > name ); free ( s ); }void setStudentAge ( Student * s , int age ) { s -> age = age ; }int getStudentAge ( const Student * s ) { return s -> age ; }char * getStudentName ( const Student * s ) { return s -> name ; }ファイルMain.c内:
#include <stdio.h>#include "Student.h"int main ( void ) { Student * s = createStudent ( 19 , "Maurice" ); char * name = getStudentName ( s ); int old_age = getStudentAge ( s ); printf ( "%sの年齢 = %i \n " , name , old_age ); setStudentAge ( s , 21 ); int new_age = getStudentAge ( s ); printf ( "%sの新しい年齢 = %i \n " , name , new_age ); destroyStudent ( s ); return 0 ; }ファイル Makefile 内:
all : out . txt ; cat $< out.txt : main ; ./$< > $@ main : Main . o Student . o Main.o Student.o : Student . h clean : ; $( RM ) *. o out . txt mainファイルStudent.cppm内:
import std ;std :: stringを使用します。class Student { private : string name ; public : Student ( const string & name ) : name { name } {}[[ nodiscard ]] const string & getName () const noexcept { return name ; }void setName ( const string & name ) noexcept { this -> name = name ; } };この例は、C#のプロパティの概念を示しています。プロパティは、クラスメンバーの特殊なタイプです。Java とは異なり、明示的なメソッドは定義されていません。パブリックな「プロパティ」に、アクションを処理するロジックが含まれています。組み込みの (宣言されていない) 変数の使用に注意してくださいvalue。
public class Student { private string name ;/// <summary> /// 学生の名前を取得または設定します/// </summary> public string Name { get { return name ; } set { name = value ; } } }後の C# バージョン (.NET Framework 3.5 以降) では、この例はプライベート変数を宣言せずに次のように省略できますname。
public class Student { public string Name { get ; set ; } }省略構文を使用すると、基となる変数はクラス内からアクセスできなくなります。そのため、set代入するにはプロパティの一部が存在する必要があります。アクセスは、特定のアクセス修飾子を使用して制限できますset。
public class Student { public string Name { get ; private set ; } }Common Lisp Object Systemでは、クラス定義内のスロット仕様で、リーダーメソッド、セッターメソッド、アクセサメソッド (リーダーメソッドと対応するメソッド) を定義するために:reader、、、オプションのいずれ:writerか(複数回でも) を指定できます。[ 3 ]スロットは常に、およびを使用して名前で直接アクセスでき、スロットアクセサオプションは、を使用する特殊なメソッドを定義します。[ 4 ]:accessorsetfwith-slotsslot-valueslot-value
CLOS自体にはプロパティの概念はありませんが、MetaObject Protocol拡張機能では、オプションで生成されたものを含め、スロットのリーダー関数名とライター関数名にアクセスする手段が指定されています:accessor。[ 5 ]
以下の例は、これらのスロットオプションと直接スロットアクセスを使用した学生クラスの定義を示しています。
( defclass student () (( name :initarg :name :initform "" :accessor student-name ) ; student-name は setf で設定可能( birthdate :initarg :birthdate :initform 0 :reader student-birthdate ) ( number :initarg :number :initform 0 :reader student-number :writer set-student-number )));; 計算プロパティのゲッターの例(これは単なるメソッドです)(defmethod student-age (( self student )) ( - ( get-universal-time ) ( student-birthdate self )));; 計算プロパティセッター内でのスロットへの直接アクセスの例( defmethod ( setf student-age ) ( new-age ( self student )) ( with-slots ( birthdate ) self ( setf birthdate ( - ( get-universal-time ) new-age )) new-age ));; スロットアクセスオプションはメソッドを生成するため、さらにメソッド定義が可能になります( defmethod set-student-number :before ( new-number ( self student )) ;; また、new-number を持つ学生が既に存在するかどうかを確認することもできます。( check-type new-number ( integer 1 * )))D言語はゲッター関数とセッター関数の構文をサポートしています。言語バージョン2では、ゲッタークラス/構造体メソッドとセッタークラス/構造体メソッドには@property属性が必要です。[ 6 ] [ 7 ]
class Student { private string name_ ;// ゲッター@property string name () { return this . name_ ; }// セッター@property string name ( string name_in ) { return this . name_ = name_in ; } }インスタンスStudentは次のように使用できます。
Student s = new Student ; s.name = "David" ; // student.name("David") と同じ効果string student_name = s.name ; // student.name ( )と同じ効果これは、プライベートフィールドにアクセスするためのパブリックプロパティの概念を示す、Delphi言語のシンプルなクラスです。
インタフェースtype TStudent = class strict private FName : string ; procedure SetName ( const Value : string ) ; public /// <summary> /// 学生の名前を取得または設定します。/// </summary> property Name : string read FName write SetName ; end ;// ...実装procedure TStudent.SetName ( const Value : string ) ; begin FName : = Value ; end ;終わり。名前だけが格納された学生を表すこの単純なクラスの例では、変数nameはプライベート、つまり Student クラスからのみ参照可能であり、「セッター」と「ゲッター」はパブリック、つまり「getName()」と「 」setName(name)メソッドであることがわかります。
public class Student { private String name ;public String getName ( ) { return name ; } public void setName ( String name ) { this.name = name ; } }この例では、コンストラクタ関数をStudent使用して、名前のみが格納された学生を表すオブジェクトを作成します。
function Student ( name ) { var _name = name ;this.getName = function ( ) { return _name ; } ;this.setName = function ( value ) { _name = value ; } ; }または(Webブラウザでアクセサーを定義する非推奨の方法を使用):[ 8 ]
function Student ( name ){ var _name = name ; this . __defineGetter__ ( 'name' , function () { return _name ; }); this . __defineSetter__ ( 'name' , function ( value ) { _name = value ; }); }または(継承のためのプロトタイプとES6アクセサー構文を使用する場合):
function Student ( name ) { this._name = name ; }Student.prototype = { get name ( ) { return this._name ; } , set name ( value ) { this._name = value ; } } ;または(プロトタイプを使用しない場合):
var Student = { get name () { return this . _name ; }, set name ( value ) { this . _name = value ; } };または(definePropertyを使用する場合):
function Student ( name ) { this._name = name ; } Object.defineProperty ( Student.prototype , ' name ' , { get : function ( ) { return this._name ; } , set : function ( value ) { this._name = value ; } } ) ;package { public class Student { private var _name : String ; public function get name () : String { return _name ; }public function set name ( value : String ) : void { _name = value ; } } }Ubuntu 12.04上のGNUstepで動作するものと同様の、手動参照カウントを使用した従来のObjective-C 1.0 構文を使用します。
@interface Student : NSObject { NSString * _name ; }- ( NSString * ) name ; - ( void ) setName: ( NSString * ) name ;@終わり@implementation学生- ( NSString * ) name { return _name ; }- ( void ) setName: ( NSString * ) name { [ _name release ]; _name = [ name retain ]; }@終わりMac OS X 10.6、iOS 4、Xcode 3.2で使用されている新しいObjective-C 2.0構文を使用して、上記と同じコードを生成します。
@interface Student : NSObject@property ( nonatomic , retain ) NSString * name ;@終わり@implementation学生@synthesize name = _name ;@終わりOS X 10.8およびiOS 6以降、 Xcode 4.4以降を使用する場合、構文はさらに簡略化できます。
@interface Student : NSObject@property ( nonatomic , strong ) NSString * name ;@終わり@implementation学生//ここには何も入っていなくても大丈夫です。@終わりパッケージStudent ;sub new { bless {}, shift ; }sub set_name { my $self = shift ; $self -> { name } = $_ [ 0 ]; }sub get_name { my $self = shift ; return $self -> { name }; }1 ;または、Class::Accessor を使用する
package Student ; use base qw(Class::Accessor) ; __PACKAGE__ -> follow_best_practice ;学生-> mk_accessors ( qw(name) );1 ;または、Mooseオブジェクトシステムを使用する:
package Student ; use Moose ;# Moose は属性名をセッターとゲッターとして使用しますが、reader および writer プロパティを使用すると、それをオーバーライドして独自の名前を指定できます。この場合、get_name と set_name には'name' => ( is => 'rw' 、isa => 'Str' 、reader => 'get_name' 、writer => 'set_name' ) があります。1 ;PHPは__getオブジェクトのプロパティに対して「マジックメソッド」を定義します__set。[ 9 ]
名前だけが格納された学生を表すこの単純なクラスの例では、変数nameはプライベート、つまり Student クラスからのみ参照可能であり、「セッター」と「ゲッター」はパブリック、つまりメソッドであることがgetName()わかりますsetName('name')。
class Student { private string $name ;/** * @return string 名前。 */ public function getName () : string { return $this -> name ; }/** * @param string $newName 設定する名前。 */ public function setName ( string $newName ) : void { $this -> name = $newName ; } }この例では、1つの変数、ゲッター、およびセッターを持つPythonクラスを使用します。
クラスStudent : _name : str# 初期化関数def __init__ ( self , name : str ) -> None : # 学生の名前を保持するインスタンス変数self . _name = name# ゲッターメソッド@property def name ( self ) -> str : return self . _name#セッターメソッド@ name.setter def name ( self , new_name : str ) - > None : self._name = new_name学生: Student = Student ( "Bob" ) print ( student.name ) #出力: Bobstudent.name = "Alice " print ( student.name ) #出力: Alicestudent._name = " Charlie " # セッターをバイパスprint(student._name) #ゲッターをバイパス#出力: CharlieRacketでは、オブジェクトシステムはモジュールやユニットに加えて、コードを整理するための手段です。他の言語と同様に、オブジェクトシステムも第一級の値を持ち、レキシカルスコープを使用してオブジェクトやメソッドへのアクセスを制御します。
#lang racket ( define student% ( class object% ( init-field name ) ( define/public ( get-name ) name ) ( define/public ( set-name! new-name ) ( set! name new-name )) ( super-new )))( define s ( new student% [ name "Alice" ])) ( send s get-name ) ; => "Alice" ( send s set-name! "Bob" ) ( send s get-name ) ; => "Bob"構造体定義は、新しい型の値を定義する別の方法であり、ミューテーターは明示的に必要な場合にのみ存在します。
#lang racket ( struct student ( name ) #:mutable ) ( define s ( student "Alice" )) ( set-student-name! s "Bob" ) ( student-name s ) ; => "Bob"Rubyでは、個別のアクセサメソッドとミューテーターメソッドを定義することも、メタプログラミング構造を使用しattr_readerてattr_accessorクラス内のプライベート変数を宣言し、それぞれ読み取り専用または読み書き可能なパブリックアクセスを提供することもできます。
個々のアクセサメソッドとミューテーターメソッドを定義することで、データの前処理や検証を行う余地が生まれます。
class Student def name @name enddef name = ( value ) @name = value end end@name暗黙の変数への読み取り専用の単純なパブリックアクセス
クラスStudent attr_reader :name end@name暗黙の変数への読み書き可能な単純なパブリックアクセス
クラスStudent attr_accessor :name endstruct Student { name : String , }impl Student { fn name ( & self ) -> & String { & self . name }fn name_mut ( & mut self ) - > & mut String { & mut self.name } }age: aNumber " 受信者の年齢が 0 より大きく 150 より小さい場合は、aNumber に設定します " ( aNumber between: 0 and: 150 ) ifTrue: [ age := aNumber ] class Student { private var _name : String = ""var name : String { get { return self . _name } set { self . _name = newValue } } }この例は、クラス内で使用されるプロパティというVB.NETの概念を示しています。C#と同様に、メソッドが明示的に使用されていGetますSet。
公開クラスの生徒プライベート_name As StringPublic Property Name () Get Return _name End Get Set ( ByVal value ) _name = value End Set End PropertyエンドクラスVB.NET 2010 では、自動実装プロパティを使用すると、Get および Set 構文を使用せずにプロパティを作成できます。コンパイラによって、_nameプロパティに対応する非表示変数 が作成されますname。クラス内で という名前の別の変数を使用すると_name、エラーが発生します。基となる変数への特権アクセスは、クラス内から可能です。
公開クラスStudent公開プロパティname As String終了クラス{{cite web}}: CS1メンテナンス: アーカイブサービスは非推奨になりました (リンク)