コンピュータプログラミングの統一アクセス原則は、ベルトラン・メイヤーによって提唱されました(元々は彼の著書『オブジェクト指向ソフトウェア構築』の中で)。この原則は、「モジュールによって提供されるすべてのサービスは、ストレージによって実装されているか計算によって実装されているかを露呈しない統一表記法によって利用可能であるべきである」と述べています。[ 1 ] [ 2 ]この原則は、一般的にオブジェクト指向プログラミング言語の構文に適用されます。より簡単に言えば、オブジェクトの属性、事前計算されたプロパティ、またはメソッド/クエリを操作する際に、構文上の違いがあってはならないということです。
ほとんどの例は原理の「読み取り」側面(つまり、値の取得)に焦点を当てていますが、MeyerはEiffelプログラミング言語の公式ウェブサイトの月刊コラムで、原理の「書き込み」の意味(つまり、値の変更)は扱いが難しいことを示しています。[ 3 ]
Meyer が取り組んでいる問題は、大規模なソフトウェア プロジェクトやソフトウェア ライブラリの保守に関するものです。ソフトウェアの開発や保守を行う際、多くのコードが記述された後で、単なる属性アクセスをメソッド呼び出しに変換するような方法でクラスやオブジェクトを変更する必要が生じる場合があります。プログラミング 言語では、属性アクセスとメソッド呼び出しに異なる構文が使用されることがよくあります (例:object.somethingとobject.something())。構文の変更は、当時普及していたプログラミング 言語では、属性が使用されているすべての箇所でソース コードを変更する必要がありました。これは、非常に大量のソース コード全体にわたって、多くの異なる箇所でソース コードを変更する必要があることを意味します。さらに悪いことに、変更が数百の顧客が使用するオブジェクト ライブラリで行われる場合、各顧客は自分のコードで属性が使用されているすべての箇所を見つけて変更し、プログラムを再コンパイルする必要があります。
逆方向(メソッドから単純な属性へ)に進むことは実際には問題ではなく、関数をそのまま残して属性値を返すようにすればよいだけです。
マイヤーは、ソフトウェア開発者が、オブジェクト属性をメソッド呼び出しに変換する、あるいはその逆といった変更によって生じる連鎖的なコード変更を最小限に抑える、あるいは排除するようなコードを書く必要性を認識した。そのため、彼は統一アクセス原則を開発した。
多くのプログラミング言語は厳密にはUAPをサポートしていませんが、その形式をサポートしています。多くのプログラミング言語で提供されているプロパティは、MeyerがUAPで解決しようとしていた問題を別の方法で解決します。プロパティは、単一の統一表記法を提供する代わりに、属性アクセスに使用されるのと同じ表記法を使用してオブジェクトのメソッドを呼び出す方法を提供します。メソッド呼び出しの構文は、引き続き利用可能です。
言語がメソッド呼び出し構文を使用する場合、以下のような形式になります。
// print は、括弧の有無にかかわらず、渡された変数を表示するものとします // Fooの属性「bar」の値を5に設定します。 Foo.bar(5) print Foo.bar()
実行すると、以下が表示されるはずです 。
5
Foo.bar(5)関数を呼び出すのか、単に属性を設定するのかは、呼び出し元からは隠蔽されています。同様にFoo.bar()、単に属性の値を取得するのか、関数を呼び出して返される値を計算するのかは、呼び出し元からは隠蔽された実装の詳細です。
言語が属性構文を使用する場合、構文は次のようになります。
Foo.bar = 5 print Foo.bar
メソッドが呼び出されたかどうか、あるいは単に値が属性に割り当てられただけかどうかは、呼び出し元のメソッドからは隠蔽されます。
しかし、UAP自体が、アクセス方法間の違いが無視できない場所、例えば返される値の計算にコストがかかる場合やキャッシュ操作がトリガーされる場合などで使用されると、問題を引き起こす可能性があります。[ 2 ]
Pythonのプロパティを使用すると、属性にアクセスするのと同じ構文でメソッドを呼び出すことができます。MeyerのUAPでは、属性アクセスとメソッド呼び出しの両方に単一の表記法(メソッド呼び出し構文)が用いられていましたが、プロパティをサポートする言語では、属性アクセスとメソッドアクセスにそれぞれ別の表記法が用いられています。プロパティを使用することで、属性表記法を用いながらも、単に値を取得または設定するのではなく、メソッドが呼び出されていることを隠蔽することができます。
そのため、Python では UAP への準拠は個々のプログラマに委ねられています。組み込み関数は、任意のメソッドを属性アクセス構文で装飾する@property簡単な方法を提供し、メソッド呼び出しと属性アクセスの構文上の違いを抽象化します。[ 4 ]
Pythonでは、Egg以下のように、重みと色が単純な属性として定義されるオブジェクトにアクセスするコードが存在する可能性があります。
""" >>> egg = Egg(4.0, "white") >>> egg.color = "green" >>> print(egg) Egg(4.0, green) """class Egg : def __init __ ( self , weight , color ) - > None : self.weight = weight self.color = colordef __str__ ( self ) -> str : return f " { __class__ . __name__ } ( { self . weight } , { self . color } )"あるいは、Eggオブジェクトはプロパティを使用し、代わりにgetterメソッドとsetterメソッドを呼び出すこともできます。
# ...(省略)... class Egg : def __init__ ( self , weight_oz : float , color_name : float ) -> None : self . weight = weight_oz self . color = color_name@property def color ( self ) -> str : """卵の色。"" return to_color_str ( self . _color_rgb )@ color.setter def color ( self , color_name : str ) - > None : self._color_rgb = to_rgb ( color_name )@property def weight ( self ) -> float : """オンス単位の重量。""" return self . _weight_gram / 29.3@ weight.setter def weight ( self , weight_oz : float ) - > None : self._weight_gram = 29.3 * weight_oz# ...(省略)...どちらの方法Eggが定義されているかにかかわらず、呼び出し元のコードは変更できません。実装は、EggEgg クラスを使用するコードに影響を与えることなく、一方の形式から他方の形式に切り替えることができます。UAP を実装する言語も、この特性を備えています。
次の点を考慮してください。
y = Egg.new ( " Green " ) y.color = " White " puts y.colorEggクラスは次のように定義できます。
class Egg attr_accessor :color def initialize ( color ) @color = color end end上記の最初のコードセグメントは、Egg がこのように定義されている場合でも問題なく動作します。Egg クラスは、以下のように定義することもできます。この場合、color はメソッドです。Egg が以下のように定義された場合でも、呼び出し元のコードは変更せずに動作します。
class Egg def initialize ( color ) @rgb_color = to_rgb ( color ) enddef color to_color_name ( @rgb_color ) end def color= ( color ) @rgb_color = to_rgb ( color ) endprivate def to_rgb ( color_name ) ..... enddef to_color_name ( color ) .... end endは、colorあるケースでは属性のように見え、次のケースではメソッドのペアのように見えますが、クラスへのインターフェースは同じままです。 Egg クラスを保守する人は、呼び出し元のコードを壊す心配なく、一方の形式から他方の形式に切り替えることができます。 Ruby は改訂された UAP に従っており、 は のアクセサ/セッター メソッドを生成するための構文糖衣attr_accessor :colorとしてのみ機能します。 Ruby では、オブジェクトのメソッドを呼び出さずにインスタンス変数を取得する方法はありません。color
厳密に言えば、Rubyは属性にアクセスするための構文とメソッドを呼び出すための構文が異なるため、Meyerのオリジナルの統一アクセス原則(UAP)には従っていません。しかし、Rubyでは、属性へのアクセスは常に関数を介して行われ、その関数は多くの場合自動的に生成されます。つまり、どちらのアクセス方法でも関数が呼び出されるため、RubyはMeyerの改訂版統一アクセス原則に従っていると言えます。
C #言語はクラスプロパティをサポートしており、これによりメンバ変数に対する操作(ゲッターとセッター)を定義できます。プロパティにアクセスgetまたは変更するための構文は、他のクラスメンバ変数にアクセスする場合と同じですが、実際の実装は、単純な読み書きアクセスまたは関数型コードとして定義できます。set
public class Foo { private string _name ;// プロパティpublic int Size { get ; // ゲッターset ; // セッター}// プロパティpublic string Name { get { return _name ; } // ゲッターset { _name = value ; } // セッター} }上記の例では、クラスには と というFoo2 つのプロパティが含まれています。プロパティ は読み取り (get) および書き込み (set) が可能な整数です。同様に、プロパティ も読み取りおよび変更が可能な文字列ですが、その値は別の (プライベート) クラス変数 に格納されます。SizeNameSizeName_name
プロパティ定義で操作を省略すると、setプロパティは読み取り専用になり、get操作を省略すると、プロパティは書き込み専用になります。
プロパティの使用には、以下のコードに示すようにUAPが使用されます。
public Foo CreateFoo ( int size , string name ) { var foo = new Foo (); foo . Size = size ; // プロパティセッターfoo . Name = name ; // プロパティセッターreturn foo ; }C++には UAP もプロパティもありません。オブジェクトが変更され、属性 (color) が関数 ( getA、setA ) のペアになった場合、オブジェクトのインスタンスを使用して属性値 (またはx = obj.color)を設定または取得する箇所はすべて、いずれかの関数 (または)obj.color = xを呼び出すように変更する必要があります。テンプレートと演算子オーバーロードを使用すると、プロパティを偽装できますが、プロパティを直接サポートする言語よりも複雑です。これは C++ プログラムの保守を複雑にします。C++ オブジェクトの分散ライブラリは、メンバデータへのアクセス方法に注意する必要があります。x = obj.getColor()obj.setColor(x)
JavaScript は2009 年のECMAScript 5以降、ゲッターとセッターによる計算プロパティのサポートを提供しています。[ 5 ]ただし、 ECMAScript 5 より前にlength存在したプロパティArray.prototypeなど、特殊なケースもあります。[ 6 ]String.prototype
ECMAScript 5 JavaScriptでは、Object.definePropertyゲッターとセッターを定義するために使用できます。[ 7 ]例:
var obj = {};Object.defineProperty ( obj , " two" , { "get" : function () { return 1 + 1 ; } } ) ;その後、obj.twoゲッター関数を実行するために使用できます。プロパティにはtwoセッターが定義されていないため、プロパティへの書き込みは何も操作を引き起こしません。セッターには、set代わりにプロパティが使用されます。
var obj = {};Object.defineProperty ( obj , " two " , { "get" : function ( ) { return this.x ; } , "set" : function () { this.x = 1 + 1 ; } } ) ;