コンピュータサイエンスにおいて、ミューテーターメソッドは変数の変更を制御するために使用されるメソッドです。これらはセッターメソッドとしても広く知られています。多くの場合、セッターにはゲッターが付随し、ゲッターはプライベートメンバー変数の値を返します。これらは総称してアクセサーとも呼ばれます。
ミューテーター メソッドは、カプセル化の原則に従って、オブジェクト指向プログラミングで最もよく使用されます。この原則によると、クラスのメンバー変数は他のコードから隠して保護するためにプライベートにされ、パブリック メンバー関数 (ミューテーター メソッド) によってのみ変更できます。パブリック メンバー関数は、必要な新しい値をパラメーターとして受け取り、必要に応じてそれを検証し、プライベートメンバー変数を変更します。ミューテーター メソッドは、代入演算子のオーバーロードに似ていますが、通常はオブジェクト階層の異なるレベルに表示されます。
ミューテーター メソッドは、非オブジェクト指向環境でも使用できます。この場合、変更する変数への参照が、新しい値とともにミューテーターに渡されます。このシナリオでは、コンパイラーは、コードがミューテーター メソッドをバイパスして変数を直接変更することを制限できません。変数がミューテーター メソッドを通じてのみ変更され、直接変更されないようにするのは、 開発者の責任です。
プロパティをサポートするプログラミング言語では、プロパティはカプセル化の有用性を犠牲にすることなく便利な代替手段を提供します。
以下の例では、完全に実装されたミューテーター メソッドは、入力データを検証したり、イベントをトリガーするなどの追加のアクションを実行したりすることもできます。
意味合い
ミューテーターとアクセサー メソッド、またはプロパティブロックを定義する代わりに、インスタンス変数にプライベート以外の可視性を与え、オブジェクトの外部から直接アクセスすることもできます。ミューテーターとアクセサーを使用すると、より細かいアクセス権の制御を定義できます。たとえば、ミューテーターではなくアクセサーを定義するだけで、パラメーターを読み取り専用にすることができます。2 つのメソッドの可視性は異なる場合があります。アクセサーをパブリックにして、ミューテーターを保護、パッケージ プライベート、または内部のままにしておくと便利なことがよくあります。
ミューテーターが定義されているブロックは、受信データの検証または前処理の機会を提供します。すべての外部アクセスがミューテーターを介して行われることが保証されている場合、これらの手順をバイパスすることはできません。たとえば、日付が個別のプライベートyear、month、変数によって表される場合、一貫性を保つために同じプライベート インスタンス変数に および によってアクセスしながら、day受信日付をミューテーターによって分割できます。すべてのケースで、1 から 12 までの範囲外の月の値は、同じコードによって拒否できます。
setDatesetYearsetMonth
逆に、アクセサは、内部変数の構造をカプセル化して外部モジュールから隠したまま、内部変数から有用なデータ表現を合成することを可能にします。通貨getAmountアクセサは、隠しパラメータによって定義された小数点以下の桁数を持つ数値変数から文字列を構築できますcurrency。
現代のプログラミング言語では、C#や Rubyのように、ミューテーターとアクセサーの定型文を1 行で生成する機能を備えていることがよくあります。これらの場合、検証、前処理、または合成用のコード ブロックは作成されません。これらの簡素化されたアクセサーは、単純なパブリック インスタンス変数よりもカプセル化の利点を保持していますが、システム設計が進み、ソフトウェアが保守され、要件が変化するにつれて、データに対する要求がより高度になることが一般的です。多くの自動ミューテーターとアクセサーは、最終的には個別のコード ブロックに置き換えられます。実装の初期段階でそれらを自動的に作成する利点は、より高度な機能が追加されたかどうかに関係なく、クラスのパブリック インターフェイスが同一のままであり、追加された場合でも大規模なリファクタリングが不要であることです。[1]public string Name { get; set; }attr_accessor :name
ミューテーターとアクセサーを持つパラメーターを、それらが定義されているクラス内から操作するには、追加の考慮が必要になることがよくあります。実装の初期段階では、これらのブロックに追加のコードがほとんどないかまったくない場合、プライベート インスタンス変数に直接アクセスするかどうかは関係ありません。検証、相互検証、データ整合性チェック、前処理、またはその他の高度な機能が追加されると、一部の内部アクセスでは新しいコードが使用され、他の場所ではバイパスされるという微妙なバグが発生する可能性があります。
アクセサ関数は、追加の手順が必要なため、データフィールドを直接取得または保存するよりも効率が悪くなる可能性がありますが、[2]このような関数はインライン化されることが多く、関数呼び出しのオーバーヘッドが排除されます。
例
組み立て
学生構造体年齢dd ?学生終了
.code
student_get_age procオブジェクト: DWORD mov ebx 、オブジェクトmov eax 、student.age [ ebx ] ret student_get_age endp
student_set_age procオブジェクト: DWORD 、年齢: DWORD mov ebx 、オブジェクトmov eax 、年齢mov student.age [ ebx ]、eax ret student_set_age endp
C
ファイル student.h 内:
#ifndef _STUDENT_H
#define _STUDENT_H
struct student ; /* 不透明な構造体 */ typedef struct student student ;
学生*学生_new ( int年齢、char *名前); void学生_delete (学生* s );
void student_set_age (学生* s 、int年齢); int student_get_age (学生* s ); char * student_get_name (学生* s );
#終了
ファイル student.c 内:
#include <stdlib.h> #include <string.h> #include "student.h"
構造体学生{ int年齢; char *名前; };
学生*学生_new ( int age , char * name ) {学生* s = malloc ( sizeof (学生) ); s -> name = strdup ( name ); s -> age = age ; s を返す; }
void student_delete (学生* s ) { free ( s -> name ); free ( s ); }
void student_set_age (学生* s 、int年齢) { s ->年齢=年齢; }
int student_get_age (学生* s ) {戻り値s ->年齢; }
char * student_get_name (学生* s ) {戻り値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 の旧年齢 = %i \n " , name , old_age ); student_set_age ( s , 21 ); int new_age = student_get_age ( s ); printf ( "%s の新年齢 = %i \n " , name , new_age ); student_delete ( 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
C++
ファイル Student.h 内:
#ifndef STUDENT_H
#define STUDENT_H
#include <文字列>
クラスStudent { public : Student ( const std :: string & name );
const std :: string & name () const ; void name ( const std :: string & name );
プライベート:
std ::文字列name_ ; };
#終了
ファイル Student.cpp 内:
#include "Student.h"
学生::学生( 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 とは異なり、明示的なメソッドは定義されていません。パブリックの「プロパティ」には、アクションを処理するロジックが含まれています。組み込み (宣言されていない) 変数の使用に注意してください。
value
パブリッククラスStudent {プライベート文字列名;
/// <summary>
/// 生徒の名前を取得または設定します/// </summary> public string Name { get { return name ; } set { name = value ; } } }
それ以降の C# バージョン (.NET Framework 3.5 以降) では、この例は、プライベート変数を宣言せずに、次のように省略できますname。
パブリッククラスStudent {パブリック文字列Name {取得;設定; } }
省略構文を使用すると、基になる変数がクラス内から使用できなくなります。その結果、set割り当てにはプロパティの部分が存在している必要があります。アクセスは、set固有のアクセス修飾子を使用して制限できます。
パブリッククラスStudent {パブリック文字列Name { get ; private set ; } }
コモンリスプ
Common Lisp Object Systemでは、クラス定義内のスロット仕様で:reader、、:writerおよび:accessorオプションのいずれか(複数回も可)を指定して、リーダーメソッド、セッターメソッド、およびアクセサメソッド(リーダーメソッドとそれぞれのsetfメソッド)を定義できます。[3]with-slotsスロットは常に、およびを使用して名前から直接アクセスできslot-value、スロットアクセサオプションは、を使用する特殊なメソッドを定義しますslot-value。[4]
CLOS自体にはプロパティの概念はないが、MetaObject Protocol拡張では、オプションで生成されたものも含め、スロットのリーダー関数名とライター関数名にアクセスする手段が指定されている:accessor。[5]
次の例は、これらのスロット オプションと直接スロット アクセスを使用した学生クラスの定義を示しています。
( 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 )) new-age ))
;; スロットアクセスオプションはメソッドを生成し、さらにメソッドを定義できるようにします
( defmethod set-student-number :before ( new-number ( self student )) ;; また、新しい番号の学生がすでに存在するかどうかを確認することもできます。( check-type new-number ( integer 1 * )))
だ
Dはゲッターとセッター関数の構文をサポートしています。言語のバージョン2では、ゲッターとセッターのクラス/構造体メソッドに@property属性が必要です。[6] [7]
class Student { private char [] name_ ; // ゲッター@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 言語のシンプルなクラスです。
インタフェース
type
TStudent = class strict private FName : string ; procedure SetName ( const Value : string ) ; public /// <summary> /// 学生の名前を取得または設定します。/// </summary> property Name : string read FName write SetName ; end ;
// ...
実装
手順TStudent.SetName ( const Value : string ) ; begin FName : = Value ; end ;
終わり。
ジャワ
名前のみが保存されている学生を表す単純なクラスのこの例では、変数 名getName()がプライベート、つまり Student クラスからのみ表示され、「setter」と「getter」、つまり「 」メソッドと「 」メソッドがパブリックであることがわかりますsetName(name)。
パブリッククラスStudent {プライベート文字列名;
パブリックString getName () { return name ; }パブリックvoid setName ( String newName ) { name = newName ; } }
JavaScript
この例では、コンストラクター関数をStudent使用して、名前のみが保存された学生を表すオブジェクトを作成します。
関数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アクセサー構文を使用):
関数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 ; } } ) ;
アクションスクリプト3.0
パッケージ
{
パブリッククラスStudent {プライベートvar _name : String ;パブリック関数get name () : String {戻り値 _name ; }
パブリック関数セット名(値:文字列) : void { _name = value ; } } }
オブジェクティブC
従来の Objective-C 1.0 構文を使用し、 Ubuntu 12.04上のGNUstepで動作している手動参照カウントを使用します。
@interface Student : NSObject
{
NSString * _name ; }
- ( NSString * )名前; - ( void ) setName: ( NSString * )名前;
@終わり
@実装 学生
- ( NSString * )名前{ return _name ; }
- ( void ) setName: ( NSString * ) name { [ _name release ]; _name = [ name preserve ]; }
@終わり
Mac OS X 10.6、iOS 4、Xcode 3.2で使用されている新しい Objective-C 2.0 構文を使用して、上記と同じコードを生成します。
@interface 学生 : NSObject
@property (非アトミック、保持) NSString *名前;
@終わり
@実装 学生
@synthesize名前= _name ;
@終わり
OS X 10.8およびiOS 6以降では、Xcode 4.4 以降を使用すると、構文をさらに簡素化できます。
@interface 学生 : NSObject
@property (非アトミック、強い) NSString *名前;
@終わり
@実装 学生
//ここには何も記述しなくても問題ありません。
@終わり
パール
パッケージ学生;
sub new { bless {}, shift ; }
サブset_name { my $self = shift ; $self -> { name } = $_ [ 0 ]; }
sub get_name { my $self = shift ; return $self -> { name }; }
1 ;
または、Class::Accessorを使用して
パッケージStudent ; base qw(Class::Accessor)を使用します。__PACKAGE__ -> follow_best_practice ;
学生-> mk_accessors ( qw(name) );
1 ;
または、Moose オブジェクト システムを使用します。
パッケージStudent ; Mooseを使用します;
# Moose は属性名をセッターとゲッターとして使用し、リーダーとライターのプロパティでは
それをオーバーライドして独自の名前を指定できます。この場合、get_name と set_name には
' name' => ( is => 'rw' 、isa => 'Str' 、reader => 'get_name' 、writer => 'set_name' ) があります。
1 ;
PHP の
PHPはオブジェクトのプロパティに対して「マジックメソッド」__getとを定義します。 [9]__set
名前のみが保存されている学生を表す単純なクラスのこの例では、変数 名getName()がプライベート、つまり Student クラスからのみ表示され、「setter」と「getter」、つまり メソッドとメソッドがパブリックであることがわかりますsetName('name')。
クラス Student
{
プライベート 文字列 $name ;
/**
* @return string 名前。
*/
public function getName () : string
{
return $this -> name ;
}
/**
* @param string $newName 設定する名前。
*/
public function setName ( string $newName ) : void
{
$this -> name = $newName ;
}
}
パイソン
この例では、1 つの変数、ゲッター、およびセッターを持つ Python クラスを使用します。
class Student :
# 初期化子
def __init__ ( self , name : str ) -> None :
# 生徒の名前を保持するインスタンス変数
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 # ゲッターをバイパス
Charlie
ラケット
Racketでは、オブジェクト システムは、モジュールとユニットに加えてコードを整理する方法です。言語の他の部分と同様に、オブジェクト システムにはファーストクラスの値があり、レキシカル スコープを使用してオブジェクトとメソッドへのアクセスを制御します。
#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 )))
( s ( new student% [ name "Alice" ])を定義します) ( s get-nameを送信します) ; => "Alice" ( s set-name! "Bob"を送信します) ( 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使用してクラス内のプライベート変数を宣言し、それぞれ読み取り専用または読み取り/書き込みパブリックアクセスを提供することができます。
個々のアクセサとミューテーターメソッドを定義すると、データの前処理や検証のためのスペースが確保されます。
クラスStudent def name @name終了
def name= (値) @name =値終了終了
@name暗黙の変数
への読み取り専用のシンプルなパブリックアクセス
クラスStudent attr_reader :name終了
@name暗黙の変数
への読み書き可能なシンプルなパブリックアクセス
クラスStudent attr_accessor :name終了
さび
構造体 Student { name : String , }
Studentの実装{ fn name ( & self ) -> & String { & self . name }
fn name_mut ( & mut self ) -> & mut文字列{ & mut self . name } }
雑談
age: aNumber
" が 0 より大きく 150 より小さい場合、受信者の年齢を aNumber に設定します "
( aNumber の範囲: 0 から 150まで)
ifTrue: [ age := aNumber ]
迅速
クラス Student {
プライベート var _name : String = ""
var name : String {
get {
return self._name } set { self._name = newValue } } }
ビジュアルベーシック
この例では、クラスで使用されるプロパティの VB.NET の考え方を示します。C# と同様に、GetおよびSetメソッドが明示的に使用されます。
パブリッククラスの生徒
プライベート_nameを文字列として
パブリックプロパティName () Get Return _name End Get Set ( ByVal value ) _name = value End Set Endプロパティ
授業終了
VB.NET 2010 では、自動実装プロパティを使用して、Get および Set 構文を使用せずにプロパティを作成できます。 コンパイラによって、_nameプロパティ に対応する という隠し変数が作成されることに注意してくださいname。 という名前のクラス内で別の変数を使用すると、_nameエラーが発生します。 基になる変数への特権アクセスは、クラス内から利用できます。
パブリッククラスStudentパブリックプロパティ名As String Endクラス
参照
参考文献
- ^ Stephen Fuqua (2009). 「C# 3.0 の自動プロパティ」。2011 年 5 月 13 日時点のオリジナルよりアーカイブ。2009 年 10 月 19 日閲覧。
- ^ Tim Lee (1998-07-13). 「アクセサ関数の実行時間効率」
- ^ 「CLHS: Macro DEFCLASS」。2011年3月29日閲覧。
- ^ 「CLHS: 7.5.2 スロットへのアクセス」。2011年3月29日閲覧。
- ^ 「MOP: スロット定義」。2011年3月29日閲覧。
- ^ 「関数 - Dプログラミング言語」。2013年1月13日閲覧。
- ^ 「The D Style」 。 2013年2月1日閲覧。
- ^ 「Object.prototype.__defineGetter__() - JavaScript | MDN」。developer.mozilla.org . 2021年7月6日閲覧。
- ^ 「PHP: オーバーロード - マニュアル」www.php.net . 2021年7月6日閲覧。
