2017-07-13 12 views
0

以下のOOP構造を考えてみましょう。基本クラス(インタフェース)は、いわゆる「ノード」クラスです。他の2つのクラスは 'Node'、つまり 'ElementNode'、 'TextNode'から継承します。多形のコンテナ(vector>)を使う必要があります。ある時点で、ベクトルを介して抽出された要素から、アップキャスティングによって隠された要素、例えば 'TextNode'のテキストを取得する必要があります。私の質問は、賢明な設計であれば、隠しメンバーにアクセスするために基本クラス 'Node'に仮想メソッドを追加し続けても問題ありません。別の選択肢はダウンキャスティングを利用することであろう。デザインに欠陥がありますか?それがあなたに何を示唆していますか? C++での必要に応じて基本クラス(インタフェース)に仮想メソッドを追加しても構いませんか?

例コード:

class Node { 
public: 
    Node() {}; 
    virtual ~Node() {}; 

    NodeTypes getNodeType() const { return nodeType; }; 
    virtual std::vector<std::shared_ptr<Node>> &getChildren() = 0; 
    virtual void pushChild(std::shared_ptr<Node>) = 0; 

private: 
    NodeTypes nodeType; 
    std::vector<std::shared_ptr<Node>> children; 
}; 

class ElementNode : public Node { 
public: 
    ElementNode(HtmlElements, Node*); 

    void setTag(HtmlElements); 

    //Inherited methods. 
    std::vector<std::shared_ptr<Node>> &getChildren(); 
    void pushChild(std::shared_ptr<Node>); 
    Node* getFather() const; 
}; 

class TextNode : public Node { 
public: 
    TextNode(std::string); 
    std::string getText() const; 
    void setText(std::string); 

    //Inherited methods. 
    std::vector<std::shared_ptr<Node>> &getChildren(); 
    void pushChild(std::shared_ptr<Node>); 
    Node* getFather() const; 

private: 
    std::string text; 
}; 
+1

保護されたデータを持つデザインには欠陥があると言えます。 –

+0

保護されている場合は、この場合はprivateで置き換えることができます。ここでは意味がありません。私はそれを私的に変更しました。 –

+0

ユースケースに大きく依存していますが、私は恐れています。バーチャルファンクションアプローチを好む一方で、長いチェーンの早い段階で行われる「dynamic_cast」によって可読性を大幅に向上させることができます。そのチェーンを単一の仮想関数に束ねることはより良いことですが、それでもすべての場合に適合するわけではありません。 – user4581301

答えて

2

「必要に応じて」メソッドを追加すると、最終的にSOLIDのInterface Segregation Principleと矛盾します。

ノードのすべてのクライアントがすべてのバリアントを知っている必要はありませんか?つまり、基本クラスが肥大化すると、クライアントはすべての詳細を知るようになります。これは、ロバート・マーティンがISPの定義に与えるXerox Printerの例とよく似ています。

getText()を基本クラスに追加すると、一部のノードではそのメソッドがサポートされないため、Liskov Substitution PrincipleのSOLIDに違反することになります。

問題は、ベクターに格納されているノードタイプを認識する方法です。私はあなたがリスコフ置換原理に基づいて、その部分を再考すべきだと言いたいと思います。ダウンキャスティングは2つの悪のうちの小さいものであるかもしれません。

+0

インターフェイスに新しいメソッドを無意味に追加することなく、ダウンキャスティングを避けるためにコードをリファクタリングすることができたので、ソリッドの原理について少しだけ読んでみる必要があることが判明しました。正しい方向に私を指してくれてありがとう。 –

2

答えは、設計の成熟度に依存します。

初期のデザインでは、確かに。それは簡単です。新しい抽象メソッドを基本クラスに追加するだけで、コンパイラは派生クラスでそれらを実装するのを忘れた場合に通知します。ケーキの一片。

成熟したデザインと大きなプロジェクトでは、それほど簡単ではないかもしれません。新しいメソッドが派生クラスのいくつかに意味をなさない場合はどうなりますか?インターフェイスが使用されているすべての場所でそれらが意味をなさない場合はどうすればよいでしょうか?私はそれが起こっていることを見てきました。そして、もしあれば...という答えは、あなたは特別な場合の回避策をハッキングします。すべての作業をやり直すには数カ月かかることがあります。作業が完了すると、システムは醜くなり、脆くなり、メンテナンスが難しくなります。

他のチームで使用されている図書館では、災害です。あなたは聞いたことがないプロジェクトのビルドを壊してしまいます。おそらく、新しいメジャーバージョンの変更をリリースし、新しいバージョンであるの両方をサポートしなければならないことがあります。これは、お客様の一部が変更を望まないためです。


初期の設計段階にいない限り、正しい答えは新しいインターフェイスを完全に定義することです。古いインタフェースは何らかの理由で存在していました。人々はまだその理由のためにそれを使用しています。あなたが変更したいのは、新しいの理由です。だから、新しい理由をサポートする新しいインターフェースを定義し、古いものをそのまま残すのはどうですか?新しいインターフェースはそれが理にかなっていれば古いものを継承することもできます。

関連する問題