2013-01-25 4 views
6

以下のクラスを考えてみましょう:getter/setterの設計を単純化するために、値ラッパーと演算子()のオーバーロードを使用する:危険な方法ですか?

class MyClass1 
{ 
    public: 
     double x() const {return _x;} // getter 
     double y() const {return _y;} // getter 
     double z() const {return _x*_y;} // getter 
     void x(const double var) {_x = var;} // setter 
     void y(const double var) {_y = var;} // setter 
     void z(const double var) {_x = var; _y = 1;} // setter 
    protected: 
     double _x; 
     double _y; 
}; 

MyClass1の実際の内容は、実装の詳細であるため、ゲッターとセッターは、クラスの内容を取得し、設定する統一された方法を提供し、彼らはinterdependantであっても(ここでは_zありません内部的に存在するが、ユーザにとっては、zは、xおよびyのような変数である。 xyのgetter/setterメソッドを記述する必要がないようにする今

、1はこのようにラッパーを使用することができます。

template <typename Type> 
class Wrapper 
{ 
    public: 
     constexpr Wrapper(const Type& value) {_value = value;} 
     constexpr Type& operator()() {return _value;} 
     constexpr const Type& operator()() const {return _value;} 
     constexpr void operator()(const Type& value) {_value = value;} 
    protected: 
     _value; 
}; 

をそして今、元のクラスは次のようになります。

class MyClass2 
{ 
    public: 
     Wrapper<double> x; 
     Wrapper<double> y; 
     double z() const {return x*y;} // getter 
     void z(const double var) {x = var; y = 1;} // setter 
}; 

がそれですゲッター/セッターを書く必要がないようにするには、危険な練習かよい解決策が必要ですか?

注:ここでMyClass1MyClass2は例にすぎません。私の質問は非常に "一般的"です:getter/setterが単に内部値を返す/設定するとき、提案されたWrapperによってクラスのgetter/setterを置き換えるのは危険です。

+2

実際に何かを実行する必要があると思います(検証、等)をアクセサーに与える。そうでなければ、彼らはそれらを持つという目的を打ち負かすでしょう。あなたは思いませんか? imho – imreal

+1

あなたのクラスはインターフェイスを取得/設定する必要がありますか?これらの関数がクラス不変式を維持する必要がない場合、それらをカプセル化することはほとんどありません。 – TemplateRex

答えて

1

getterとsetterの主な目的は、プライベートインスタンス変数にアクセスして設定する統一的な方法を提供することです。

あなたのラッパーソリューションから、プログラムのライフサイクルのトラックをいつか変更したい場合は、ラッパーを破棄して、MyClass1のような元のゲッター/セッターに置き換えることができます。それを呼び出すコードの他のコンポーネント

だから、あなたの解決策は余分な数行のコードを入力するのを防ぐ有効な方法だと思います。

1

特に危険なことは何も見ませんが、何も得られないようです。 C++は、セッター関数と互換性のないあらゆる種類のオブジェクトの参照セマンティクスを提供します。時には、実際の内容は単なる詳細ではない場合もあります。あなたはまた、(実際には、これは私が、私はこの質問をクリックしたときに表示することが期待したものである)他の道を行くことができ

struct virt_z { 
    double x; 
    double y; 
    struct z_wrap { 
     virt_z &parent; 

     operator double() { return parent.x * parent.y; } 
     z_wrap &operator=(double in) { 
      parent.x = in; 
      parent.y = 1; 
      return *this; 
     } 
     z_wrap(virt_z &in) : parent(in) {} 
    } z; 

    virt_z() : z(*this) {} 
    virt_z(virt_z const &o) : x(o.x), y(o.y), z(*this) {} 
}; 

https://ideone.com/qqgj3D

+1

そして、あなたの貧弱なクライアントは、このインターフェースを読んで、zを設定するために、オーバーラップした演算子=内部ラッパークラスのzに割り当てる必要があることを理解してください。 OP MyClass1の単純なgetterとsetter関数のスタイルは、インターフェイスの使用方法を読んだことが明らかなので、MyClass2とソリューションのどちらよりも優れています。 3つすべてが同じコードにコンパイルされていることを考えれば、私はMyClass1が好まれるべきだと主張する。 –

+0

@ user1131467異なる状況が異なる状況に合っています。 MyClass1に関しては、 "z'を設定する以上の機能は" SetZ() ""と呼ぶべきではありません。これはゲッター/セッターの形式主義の根本的な問題です。何かに副作用があると、あなたは本当にただの状態を設定するだけではありません。同様に、同じようなオブジェクトの中に副作用を追加するためのインタフェースをめったに変更することはめったにありません。 OOの教科書によって永続化された単なる神話です。 – Potatoswatter

+0

インターフェイスの実装の詳細についてユーザーが気になるのはなぜですか?彼らは自然にそれを使うべきです。 – balki

1

それはのような危険はありません、それはちょうどより多くであります使用するのが難しい(クラスユーザーのために)。 MyClass2はインターフェイスを難読化し、混乱させます。

コードを書くのが面倒になるかもしれませんが、コードを一度書くだけで済みます。インターフェイスは何度も再利用されます。したがって、コードリーダーを優先する必要があります。

この理由から、MyClass1はMyClass2とPotatoswatterのどちらのソリューションよりも優れています。

(これを行うには、C++でのやり方があった場合でも:

class MyClass3 
{ 
    double x, y, z; 
} 

MyClass3::z::operator=(double) { ... } // property set 
double MyClass3::z::operator() { ... } // property get 

「隠された」コードのこの種は、そのできの悪い仮定につながるので、C#で、それはまだ私が考える悪いアイデアだろうように関数のスタイルでは、少なくともプリミティブなコピーよりも複雑なものがあるかもしれないと思うことができます。

関連する問題