知識ベース

ミューテーター法

コンピューターサイエンスでは、 ミューテーターメソッドは変数の変更を制御するために使用されるメソッドです。また、 セッターメソッドとして広く知られています。多くの場合、セッターにはプライベートメンバー変数の値を返すゲッターアクセサーとも呼ばれます )が付随します。

ミューテーターメソッドは、カプセル化の原則に従って、オブジェクト指向プログラミングで最もよく使用されます。この原則によれば、クラスのメンバー変数はプライベートになり、他のコードからそれらを隠して保護します。また、パブリックメンバー関数(ミューテーターメソッド)によってのみ変更できます。そして、プライベートメンバー変数を変更します。ミューテーターメソッドは、代入演算子のオーバーロードと比較できますが、通常はオブジェクト階層の異なるレベルで表示されます。

ミューテーターメソッドは、非オブジェクト指向環境でも使用できます。この場合、変更される変数への参照は、新しい値とともにミューテーターに渡されます。このシナリオでは、コンパイラーはコードがミューテーターメソッドをバイパスして変数を直接変更することを制限できません。開発者の責任は、変数がミューテーターメソッドを介してのみ変更され、直接変更されないようにすることです。

それらをサポートするプログラミング言語では、プロパティはカプセル化のユーティリティを放棄することなく、便利な代替手段を提供します。

以下の例では、完全に実装されたミューテーターメソッドは、入力データを検証したり、イベントのトリガーなどの追加アクションを実行したりすることもできます。

含意

ミューテーターおよびアクセサーメソッド、またはプロパティブロックを定義する代わりに、インスタンス変数にプライベート以外の可視性を与え、オブジェクトの外部から直接アクセスする方法があります。ミューテーターとアクセサーを使用して、アクセス権のより詳細な制御を定義できます。たとえば、パラメータは、アクセッサを定義するだけで読み取り専用にすることができますが、ミューテータは定義しません。 2つのメソッドの可視性は異なる場合があります。ミューテーターが保護され、パッケージプライベートまたは内部のままである間に、アクセサーがパブリックであることがしばしば有用です。

ミューテーターが定義されているブロックは、受信データの検証または前処理の機会を提供します。すべての外部アクセスがミューテーターを経由することが保証されている場合、これらのステップはバイパスできません。たとえば、日付が個別の年、月、日変数で表される場合、一貫性を保つためにsetYearとsetMonthで同じプライベートインスタンス変数にアクセスしながら、着信日付をsetDateミューテーターで分割できます。すべての場合で、1〜12以外の月の値は同じコードで拒否できます。

逆に、アクセサーは、内部変数から有用なデータ表現の合成を可能にし、その構造をカプセル化して外部モジュールから隠します。通貨getAmountアクセサーは、非表示の通貨パラメーターで定義された小数点以下の桁数を持つ数値変数から文字列を作成できます。

現代のプログラミング言語では、多くの場合、ミューテーターとアクセサーのボイラープレートを1行で生成できます。たとえば、C#のパブリックストリングName {get;セットする; }およびRubyのattr_accessor:name。これらの場合、検証、前処理、または合成用のコードブロックは作成されません。これらの単純化されたアクセサーは、単純なパブリックインスタンス変数よりもカプセル化の利点を保持しますが、システム設計の進行、ソフトウェアの維持、要件の変更に伴い、データに対する要求がより洗練されることが一般的です。多くの自動ミューテーターとアクセサーは、最終的に別のコードブロックに置き換えられます。実装の初期にそれらを自動的に作成することの利点は、クラスのパブリックインターフェイスが、高度な洗練性が追加されても追加されなくても同じままであり、大規模なリファクタリングが必要ないことです。

定義されているクラスからミューテーターとアクセサーを持つパラメーターを操作するには、多くの場合、追加の考慮が必要です。実装の初期段階では、これらのブロックに追加のコードがほとんどないかまったくない場合、プライベートインスタンス変数に直接アクセスするかどうかに違いはありません。検証、相互検証、データ整合性チェック、前処理、またはその他の高度化が追加されると、一部の内部アクセスが新しいコードを使用し、他の場所ではバイパスされるというわずかなバグが発生する場合があります。

アクセサー関数は、余分な手順が含まれるため、データフィールドを直接フェッチまたは格納するよりも効率が悪い場合がありますが、そのような関数は多くの場合インライン化され、関数呼び出しのオーバーヘッドがなくなります。

組立例

学生struct age dd?学生は終わります
.code student_get_age procオブジェクト:DWORD mov ebx、object mov eax、student.age ret student_get_age endp student_set_age procオブジェクト:DWORD、age:DWORD mov ebx、object mov eax、age mov student.age、eax ret student_set_age endp

Cの例

ファイルstudent.h内:

#ifndef _STUDENT_H #define _STUDENT_H struct student; / *不透明な構造* / typedef struct student student; student * student_new(int age、char * name); void student_delete(student * s); void student_set_age(student * s、int age); int student_get_age(student * s); char * student_get_name(student * s); #endif

ファイルstudent.c内:

#include stdlib.h> #include string.h> #include "student.h" struct student {int age; char * name; }; student * student_new(int age、char * name){student * s = malloc(sizeof(student)); s-> name = strdup(name); s-> age = age;返却値; } void student_delete(student * s){free(s-> name);無料; } void student_set_age(student * s、int age){s-> age = age; } int student_get_age(student * s){return s-> age; } char * student_get_name(student * s){return s-> name; }

ファイルmain.c内:

#include stdio.h> #include "student.h" int main(void){student * s = student_new(19、 "Maurice"); char * name = student_get_name(s); int old_age = student_get_age(s); printf( "%s's old age =%i \ n"、name、old_age); student_set_age(s、21); int new_age = student_get_age(s); printf( "%s's new age =%i \ n"、name、new_age); student_delete(s); 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

C ++の例

Student.hファイル内:

#ifndef STUDENT_H #define STUDENT_H #include string> class Student {public:Student(const std :: string&name); const std :: string&name()const; void name(const std :: string&name); private:std :: string name_; }; #endif

Student.cppファイル内:

#include "Student.h" Student :: Student(const std :: string&name):name_(name){} const std :: string&Student :: name()const {return name_; } void Student :: name(const std :: string&name){name_ = name; }

C#の例

この例は、クラスメンバーの特別なタイプであるプロパティのC#の考え方を示しています。 Javaとは異なり、明示的なメソッドは定義されていません。パブリックな「プロパティ」には、アクションを処理するロジックが含まれています。組み込みの(宣言されていない)変数値の使用に注意してください。

public class Student {プライベートストリング名。 /// summary> ///学生の名前を取得または設定/// / summary> public string Name {get {return name; } set {name = value; }}}

それ以降のC#バージョン(.NET Framework 3.5以降)では、プライベート変数名を宣言せずに、この例を次のように短縮できます。

public class Student {public string Name {get;セットする; }}

短縮構文を使用すると、基になる変数はクラス内から使用できなくなります。その結果、プロパティの設定部分が割り当てのために存在する必要があります。セット固有のアクセス修飾子を使用して、アクセスを制限できます。

public class Student {public string Name {get;プライベートセット; }}

Common Lispの例

Common Lisp Object Systemでは、クラス定義内のスロット仕様は、リーダーメソッド、セッターメソッド、アクセサーメソッド(リーダーメソッドとそれぞれのsetfメソッド)を定義するために、:reader、:writer、:accessorオプションのいずれか(複数回も)を指定できます。 。スロットは、with-slotsおよびslot-valueを使用して、名前から常に直接アクセスできます。スロットアクセサオプションは、slot-valueを使用する特殊なメソッドを定義します。

CLOS自体にはプロパティの概念はありませんが、MetaObject Protocol拡張機能は、:accessorオプションで生成されたものを含む、スロットのリーダーおよびライターの関数名にアクセスする手段を指定します。

次の例は、これらのスロットオプションと直接スロットアクセスを使用した学生クラスの定義を示しています。

(defclass student()((name:initarg:name:initform "":accessor student-name); student-nameは設定可能(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 )) 新時代)) ;;スロットアクセスオプションはメソッドを生成するため、さらにメソッド定義を定義できます(defmethod set-student-number:before(new-number(self student));;また、new-numberの学生が既に存在するかどうかを確認することもできます(check- type new-number(integer 1 *))))

Dの例

Dは、getterおよびsetter関数構文をサポートしています。言語のgetterおよびsetterクラス/構造体メソッドのバージョン2では、@ property属性が必要です。

class Student {private char name_; // getter @property char name(){return this.name_; } //セッター@property char name(char name_in){return this.name_ = name_in; }}

Studentインスタンスは次のように使用できます。

auto student = new Student; student.name = "David"; // student.name( "David")と同じ効果auto student_name = student.name; // student.name()と同じ効果

Delphiの例

これは、プライベートフィールドにアクセスするパブリックプロパティの概念を示す、Delphi言語の単純なクラスです。

インターフェイスタイプTStudent = class strict private FName:string;プロシージャSetName(const Value:string); public /// summary> ///学生の名前を取得または設定します。 /// / summary> property Name:string read FName write SetName;終わり; // ...実装手順TStudent.SetName(const Value:string); begin FName:=値;終わり;終わり。

Javaの例

名前のみが保存された学生を表す単純なクラスのこの例では、変数がプライベートである、つまりStudentクラスからのみ表示され、「setter」および「getter」がパブリックである、つまり「getName( )」および「setName(name)」メソッド。

public class Student {private String name; public String getName(){名前を返す; } public void setName(String newName){name = newName; }}

JavaScriptの例

この例では、コンストラクター関数Studentを使用して、名前のみが保存されている学生を表すオブジェクトを作成します。

function Student(name){var _name = name; this.getName = function(){return _name; }; this.setName = function(value){_name = value; }; }

または(非標準):

function Student(name){var _name = name; this .__ defineGetter __( 'name'、function(){return _name;}); this .__ defineSetter __( 'name'、function(value){_name = value;}); }

または(継承にプロトタイプを使用する場合、ECMA-6!):

function Student(name){this._name = name; } Student.prototype = {get name(){return this._name; }、set name(value){this._name = value; }};

または(プロトタイプを使用せずに、ECMA-6):

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;}});

Actionscript 3.0の例

package {public class Student {private var _name:String; public function get name():String {return _name; } public function set name(value:String):void {_name = value; }}}

Objective-Cの例

Ubuntu 12.04でGNUstepに取り組んでいるものとして手動参照をカウントする従来のObjective-C 1.0構文を使用します。

@interface Student:NSObject {NSString * _name; }-(NSString *)name; -(void)setName:(NSString *)name; @end @implementation Student-(NSString *)name {return _name; }-(void)setName:(NSString *)name {; _name =; } @終わり

Mac OS X 10.6、iOS 4、およびXcode 3.2で使用されている新しいObjective-C 2.0構文を使用して、上記と同じコードを生成します。

@interface Student:NSObject @property(nonatomic、retain)NSString * name; @end @implementation Student @synthesize name = _name; @終わり

また、Xcode 4.4以降を使用しているOS X 10.8およびiOS 6から、構文をさらに簡素化できます。

@interface Student:NSObject @property(nonatomic、strong)NSString * name; @end @implementation Student //ここには何も入らず、問題ありません。 @終わり

Perlの例

パッケージStudent; sub new {bless {}、shift; } sub set_name {my $ self = shift; $ self-> {name} = $ _; } sub get_name {my $ self = shift; return $ self-> {name}; } 1;

または、Class :: Accessorを使用します

パッケージStudent;ベースqw(Class :: Accessor)を使用します。 __PACKAGE __-> follow_best_practice; Student-> mk_accessors(qw(name)); 1;

または、Moose Object Systemを使用して:

パッケージStudent; Mooseを使用します。 #Mooseは属性名をセッターとゲッター、リーダーとライターのプロパティとして使用します#これをオーバーライドして独自の名前を提供します。この場合、get_nameとset_nameには 'name' =>(is => 'rw'、isa => 'Str'、リーダー=> 'get_name'、ライター=> 'set_name'); 1;

PHPの例

名前のみが格納された学生を表す単純なクラスのこの例では、変数がプライベートである、つまりStudentクラスからのみ表示され、「setter」および「getter」がパブリックである、つまりgetName()であることがわかりますおよびsetName( 'name')メソッド。

class Student {private $ name; / ** * @return the $ name * / public function getName(){return $ this-> name; } / ** * @param $ newName *設定する名前* / public function setName($ newName){$ this-> name = $ newName; }}

Pythonの例

この例では、1つの変数、getter、およびsetterを持つPythonクラスを使用します。

class Student(object):#Initializer def __init __(self、name):#学生の名前を保持するインスタンス変数self._name = name#ゲッターメソッド@property def name(self):return self._name#セッターメソッド@name .setter def name(self、new_name):self._name = new_name
>>> bob = Student( "Bob")>>> bob.name Bob >>> bob.name = "Alice" >>> bob.name Alice >>> bob._name = "Charlie"#セッターをバイパス> >> bob._name#ゲッターチャーリーをバイパス

ラケット

ラケットでは、オブジェクトシステムはモジュールとユニットに加えて来るコードを整理する方法です。他の言語と同様に、オブジェクトシステムにはファーストクラスの値があり、オブジェクトとメソッドへのアクセスを制御するために字句スコープが使用されます。

#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%))(send s get-name); => "Alice"(set-nameを送信! "Bob")(s-get-nameを送信); =>「ボブ」

構造体の定義は、明示的に必要なときにミューテーターが存在する、新しいタイプの値を定義する代替方法です。

#lang racket(struct student(name)#:mutable)(define s(student "Alice"))(set-student-name!s "Bob")(student-name s); =>「ボブ」

ルビーの例

Rubyでは、個々のアクセサーメソッドとミューテーターメソッドを定義するか、メタプログラミング構造attr_readerまたはattr_accessorを使用して、クラスでプライベート変数を宣言し、読み取り専用または読み取り/書き込みパブリックアクセスを提供します。

個々のアクセサーおよびミューテーターメソッドを定義すると、データの前処理または検証のためのスペースが作成されます。

class Student def name @name end def name =(value)@ name = value end end

暗黙の@name変数への読み取り専用の単純なパブリックアクセス

class Student attr_reader:name end

暗黙の@name変数への読み書き可能な単純なパブリックアクセス

class Student attr_accessor:name end

Smalltalkの例

age:aNumber "0より大きく150より小さい場合、受信者の年齢をaNumberに設定します"(aNumberの間:0〜:150)ifTrue:

迅速な例

class Student {private var _name:String = "" var name:String {get {return self._name} set {self._name = newValue}}}

Visual Basic .NETの例

この例は、クラスで使用されるプロパティのVB.NETの考え方を示しています。 C#と同様に、GetメソッドとSetメソッドの明示的な使用があります。

Public Class Student Private _name As String Public Property Name()Get Return _name End Get Set(ByVal value)_name = value End Set End Property End Class

VB.NET 2010では、自動実装プロパティを使用して、GetおよびSet構文を使用せずにプロパティを作成できます。プロパティ名に対応するために、_nameという名前の隠し変数がコンパイラによって作成されることに注意してください。 _nameという名前のクラス内で別の変数を使用すると、エラーが発生します。基になる変数への特権アクセスは、クラス内から利用できます。

パブリッククラスの学生Public Property name As String End Class