問題の声明
ご存知のように、ほとんどの場合、プロパティはクラスのプライベートフィールドまたは保護フィールドを非表示にするために使用されます。 つまり、この場合のプロパティは、データのカプセル化とそれらを操作するメソッドの実装に役立ちます。 簡単な例を考えてみましょう。
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
class Car { const double MINIMAL_SPEED = 0d; const double MAX_KNOWN_CAR_SPEED = 1229.78d; private double maxSpeed; public double MaxSpeed { get { return maxSpeed; } set { if ( value < MINIMAL_SPEED || value > MAX_KNOWN_CAR_SPEED) throw new ArgumentOutOfRangeException( "MaxSpeed" ); maxSpeed = value ; } } } * This source code was highlighted with Source Code Highlighter .
この例では、Carクラスが定義されています。これは、maxSpeedフィールドに制限を課して、特定の範囲内の値のみを持つようにします。 このコードにはかなり重大な問題があります。 残念ながら、MaxSpeedプロパティは、Carクラスの外部から間違った速度を割り当てることからのみmaxSpeedフィールドを保護します。 Carクラスの他のメソッドとプロパティは、maxSpeedフィールドに任意の値を割り当てることができます。 時々それは正常であり、時にはそれは危険です。 Carクラスの次の(危険な)ファクトリメソッドを見てください。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- クラスカー
- {
- // ...
- パブリック スタティックカーCreateRandomCar()
- {
- 新しい車を返す ()
- {
- maxSpeed =( new Random ())。NextDouble()* double .MaxValue、
- };
- }
- }
明らかに、このメソッドは、定数MAX_KNOWN_CAR_SPEEDの値よりも大きい最大速度のマシンを作成できます。 したがって、タスクは、Carの外部のコードとCarクラス自体の両方からmaxSpeed変数を非表示にすることです。 クラス自体がメソッドを使用してこのフィールドにアクセスする場合でも。 そしてすでにメソッドは割り当てられた値の正確さを保証します。 悲しいことですが、すべてのプロジェクト参加者にmaxSpeedフィールドの代わりにMaxSpeedプロパティの使用を強制することは不可能です。 はい、私自身もこの種の問題分野を忘れていることに気付きました。
解決策
tru-OOPプログラミングを実践している人は、そのソリューションは単純であるため、タスクを検討する価値はなかったと言います。maxSpeedを別のクラスSpeedとビジネス全体にカプセル化します。 しかし、私の意見では、いくつかのケースでは、スズメの大砲からの射撃に似ており、各類似のプロパティの個別のクラスを開始するのはアマチュアスタイルです。 しかし、このアプローチがイデオロギー的に最もクリーンであることに同意します。
同僚と私は別の解決策を見つけました。 このソリューションには欠点といくつかの制限があります。 そのため、そのような「問題」フィールドをカプセル化するプロパティを簡単に記述できるクラスを以下に示します。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- パブリック クラス HidingProperty <T>
- {
- パブリック デリゲート T1 Getter <T1>( ref T1 currentValue);
- パブリック デリゲート void Setter <T2>( ref T2 currentValue、T2 newValue);
- private T _storedValue;
- private Getter <T> _getter;
- プライベートセッター<T> _setter;
- public HidingProperty(Getter <T>ゲッター、Setter <T>セッター)
- : this ( デフォルト (T)、getter、setter){}
- public HidingProperty(T initialValue、Getter <T> getter、Setter <T> setter)
- {
- _storedValue = initialValue;
- _getter = getter;
- _setter = setter;
- }
- public void Set(T newValue)
- {
- _setter( ref _storedValue、newValue);
- }
- パブリック T Get()
- {
- return _getter( ref _storedValue);
- }
- }
そして、これは問題のフィールドとプロパティの説明の例です:
*このソースコードは、 ソースコードハイライターで強調表示されました。
- private HidingProperty < double > NewMaxSpeed = new HidingProperty < double >(
- ( ref double currentValue)=> { return currentValue; }、
- ( ref double currentValue、 double newValue)=>
- {
- if (newValue <MINIMAL_SPEED || newValue> MAX_KNOWN_CAR_SPEED)
- throw ArgumentOutOfRangeException( "NewMaxSpeed" );
- currentValue = newValue;
- }
- );
Get()およびSet()メソッドを使用して、新しいフィールドを操作する必要があります。
var currentSpeed = mazda.NewMaxSpeed.Get()
mazda.NewMaxSpeed.Set(currentSpeed + 10d);
はい、同僚。 もちろん、これは超セクシーなコードではありません。 フィールドの例は、Javaプログラミングを思い出させます。 ただし、主な問題は解決されており、血液は比較的少ない。 これで、車の外側と車内の両方からNewMaxSpeedにアクセスする場合、指定された範囲内の新しい値の発生に対して同じチェックが実行されます。
短所、制限、副作用
言語自体のプロパティでフィールドを完全に非表示にする可能性を確認したいと思います。 私の同僚は、この場合のこの構文のようなものを見ています:
*このソースコードは、 ソースコードハイライターで強調表示されました。
- パブリック ダブルスピード
- {
- 倍速;
- get { //ゲッターコード }
- set { //セッターコード }
- }
私が引用したのと同じオプションにより、厄介なGetメソッド、Setメソッドを介してフィールドを操作し、直接割り当てを回避できます。 私はこれを克服することができませんでした。 暗黙のキャスト演算子を記述すると、double x = car.MaxSpeed;と書くことができます。 簡単に言えば、car.MaxSpeed = 10dを使用する可能性を認識してください。 それは不可能であることが判明しました。
提案されたソリューションに関連するもう1つの問題は、ゲッターとセッターに異なるアクセス修飾子を指定できないことです。もちろん、これは大きなマイナスです。
私はそのようなフィールド/プロパティをシリアル化する問題さえ考慮しませんでした。 困難があると思います。 HidingPropertyクラスのSerializable属性を単にハングさせるだけで十分である可能性を排除しませんが。
結論
私たちのプロジェクトの枠組みの中で、同僚と私がこの決定に立ち向かう頻度を言うのは難しいです。 ただし、コードのスタイルについてはあまり気にしません。宿題でクラスを使用します。
ご清聴ありがとうございました! この決定に対する批判と新しい考えに感謝します。