2009-07-17 13 views
1

私はいくつかの私的な内部構造を持つクラスウィジェットを持っていますが、私はこれらの内部構造を友人関係を介してファンクションツール(唯一)に公開したいと考えています。クラス内部を将来の述語ファンクタに公開するための優れた設計メカニズムですか?

class Widget 
    { 
    ComponentA a; 
    ComponentB b; 
    friend class WidgetPredicate; 
    }; 

class WidgetPredicate : public std::unary_function<bool, const Widget&> 
    { 
    bool operator() (const Widget& w) const 
    { 
    // inspect a and b and make a yes/no decision 
    } 
    }; 

意図は、私はすべてのstdでふるいとしてWidgetPredicate機能を使用することができます::アルゴリズムの家族xxxx_ifです。

しかし、私は将来、開発者が何らかの形できちんと友人のステータスをinheirit新しい述語を作ることができるシステムで、いくつかの拡張性を残したいと思います。これには良いデザインのイディオムはありますか?私の現在の(非)解決策は、ウィジェットのすべてが公開されていることです。

私は、保護された機能を通じてWidgetPredicateのサブタイプへのウィジェットの内部を露出したベースクラスの周りいじるような何か試してみました:私は今、すべてのクラスはウィジェットの内部を検査する可能性のある潜在的に暮らすことができる

class WidgetPredicate : public std::unary ... 
    { 
    protected: 
    const ComponentA& expose_a (const Widget& w) const { return w.a; } 
    const ComponentB& expose_b (const Widget& w) const { return w.b; } 
    public: 
    virtual bool operator() ... 
    }; 

class DerivedWidgetPredicate : public WidgetPredicate 
    { 
    public: 
    virtual bool operator() (const Widget& w) 
     { 
     const ComponentA& a = this->expose_a(w); // a can now be inspected/predicated upon 
     const ComponentB& b = this->expose_b(w); // b ... 
     } 
    }; 

を単に:public WidgetPredicateと言ってください。私は悪意のある開発者から保護しようとしているわけではないので、フレンドリーなWidgetPredicateだと言うと、私はそれを額面で喜んで受け取ります。 構文もsubparですが、私もそれで生きることができます。メンテナンスの問題が気に入らないのですが、ウィジェットの内部を変更するときには、新しいexpose()メソッドをベース述語に追加する必要があります。 (そのプロセスは機械的であり、機械的でなければならない)。

私はいつもWidgetクラスに直接公開する方法を置くことができますが、IMOその時点で私は同様のフィールドを公開する可能性があり、それ(つまり私の現在のSOLN)と一緒に住んでいます。

答えて

2

それはより高価ですが、あなたは操作が外部コードに許可することができるかを定義し、それらを実装する必要があります。パブリック/保護されたインターフェイスでこれらの操作を提供したくない場合は、投稿したソリューション(別のクラスをfriendとして宣言し、それを内部コードのプロキシとして使用する)は良い解決策です。

これは主観的で、おそらく望ましくないコメントですが、とにかく... 良いデザインのようなものはありません。クラスの内部を公開するメカニズムです。

<皮肉>ただ、すべてが公開する< /皮肉>

あなたがやっていることはあなたのカプセル化を破る複雑な機構を開発しています。あなたがそれを終えると、すべてのコードがあなたのプライベートメンバーにアクセスできます。あなたはのみ述語ファンクタはメンバーへアクセスできますが、あなただけのデータへのアクセスを取得するために述語タイプからを導出から非述語のクラスをブロックすることはできませんと述べています。

カプセル化のポイントの1つは、依存性を減らすことです。外部コードはパブリックインターフェイスのみに依存し、内部の型と表現を変更することができ、外部コードは破損しません(外部コードnot class or friend)。今では、あなたが(直接的または間接的に)あなたの内部を公開した後、それらは今あなたの公開インタフェースの一部であり、したがって凍結されなければなりません。

カプセル化のもう一つのポイントは、あなたが内部データの不変式を保証することができるということです。外部コードが内部にアクセスした後は、あなたのメソッドで不変なものを制御することはできません(コンテナのフィールドを取得されたメモリのサイズに維持するなど)。

推移的でも継承的でもない友情の理由があります。クラスをクラスの友人に宣言すると、そのクラスの動作を確認できます(ほとんどの場合、友人クラスを完全に制御しています) )。あなたは、あなたがアクセス権を与えているコードが自分のコードを壊さないことを知っています。

class Widget 
{ 
    friend class BackDoor; // assume that you have implemented it 
}; 
class MaliciousClass1 // third class, malicious or not cannot break into Widget 
{ 
}; 
class MaliciousClass2 : public BackDoor // You cannot block external code from deriving from BackDoor 
{ 
    // Can break into your widget, access and modify anything 
}; 

ドアは誰でもあなたの私的な部分で遊ぶことができます。

+0

とても素敵で正確な答えです!私の+1。 –

+0

マイナーノート:BackDoorオブジェクトIIUCを作成するためにプライベートctorとpublic staticファクトリ関数を使用して派生をブロックすることができます。 – Macke

+0

@Marcus Lindblom:継承をブロックする方法はいくつかありますが、これに限定されません。しかし、友情は推移的でも継承的でもないので、私はそれがこの答えにどのように関係しているか分かりません。継承をブロックするもう1つのアプローチは、各コンストラクタに静的メソッドを記述する必要はありません。http://stackoverflow.com/questions/656224/when-should-i-use-c-private-inheritance/656523#656523 –