2016-04-02 4 views
1

私はプログラミングにかなり新しいので、答えが明らかであれば私の質問に耐えます。Lippman's C++ Primerのデザインパターン

Lippman、Lajoie、およびMooのC++ Primer、5th ed。の15.9節では、継承階層のプログラミングが行われています。基本的には抽象クラスQuery_baseで、その子はWordQuery,NotQuery、およびBinaryQueryであり、最後はさらにAndQueryOrQueryに分岐します。 Query_base階層は、ポインタを介してQueryというインターフェイスクラスによってアクセスされます。

Query q = Query("fiery") & Query("bird") | Query("wind")

(これらの演算子はQuery_base型のオブジェクトを指すQueryオブジェクト、例えばを生成し、「&」はAndQueryを生成し、 ':このセットアップの基本的な考え方は、このような化合物のクエリを使用することです~NotQueryを生成します。

テキスト文書は、クエリのルールを満たす行を検索することができます。)

次のようにクラスQuery_baseです:

class Query_base { 
    friend class Query; 
protected: 
    virtual ~Query_base() = default; 
private: 
    virtual QueryResult eval(const TextQuery&) const = 0; 
    virtual std::string rep() const = 0; 
} 

すべてのQuery_baseの子はeval()とrep()を実装するため、インスタンス化できます。

次のようにクラスのクエリが設計されています:

class Query { 
    friend Query operator~(const Query&); 
    friend Query operator|(const Query&, const Query&); 
    friend Query operator&(const Query&, const Query&); 
public: 
    Query(const std::string&); 
    QueryResult eval(const TextQuery &t) const { return q->eval(t); } 
    std::string rep() const { return q->rep(); } 
private: 
    Query(std::shared_ptr<Query_base> query) : q(query) { } 
    std::shared_ptr<Query_base> q; 

だから私の理解がデザインはその後、継承階層と故意に互いに分離されているインタフェースを持っていることです。後者は、基底クラスをインタフェースとして提供するのではなく、前者を側からアクセスします。

私は2つの質問があります。

  1. は別々のQueryクラスを持っていないに何か問題はありますか? Query_baseクラスを階層のベースとインターフェイスの両方として機能させ、Queryクラスを取り除くことはできますか?私はQuery_baseということを認識していますが、それは純粋な仮想関数を持っているためできませんが、これらは変更することができます。たとえば、eval()Query機能とrep()機能の実装をQuery_baseに移動できませんでしたか?それに何か悪いことはありますか? のQueryは問題を複雑にし、分離の基礎となりますか?もしそうなら、私たちはその問題を回避できますか?
  2. このような階層インターフェイスの分離があるのはどのくらいですか(これについて考えるのが正しい方法であれば)。事前に

ありがとう!

+0

継承がないため、Query_baseはQueryの基本クラスではありません。 (悪い)名前にもかかわらず、これらのクラスの間には全く関係がありません。さもなければ、 'friend'キーワードが必要であった理由。本の中で "カプセル化"を調べてください。 –

+0

明確にする: 'Query'は' Query_base'継承階層のすべてではないことを理解します。そのため、あなたが言うように、 'Query_base'クラスでは' friend Query'が必要です。実際には、 'Query'と' Query_base'の間のこの分離は私の質問についてです。 – Marron

答えて

0

これらの演算子と他のサブクエリ型を引き続き実装すると、分離の必要性が明らかになります。例えば、&のように見えるかどうかを想像してみましょう。

&オペレータはクエリを作成する必要があります。クエリは2つのクエリを含む必要があります。 ので、AndQueryクラスは次のように定義することができます。

class AndQuery: public BinaryQuery { 
public: 
    AndQuery(const Query_base& lhs, const Query_base& rhs); 
    AndQuery(Query_base&& lhs, Query_base&& rhs); 
    ... 
    QueryResult eval(const TextQuery& t) override; 
private: 
    // required members and private methods... 
    // two principal members, lhsQuery and rhsQuery for 
    // left hand side subquery and light hand side subquery 
    // correspondingly are defined in `BinaryQuery` 
}; 

と、シンプルな最適化されていない場合には、AndQueryは何とかこのような検索(eval())実行する必要があります。

QueryResult AndQuery::eval(const TextQuery& t) { 
    QueryResult lhsResult = lhsQuery.eval(t); 
    QueryResult rhsResult = rhsQeury.eval(t); 
    // the resulting ResultSet should include only entries 
    // from both result sets 
    for(auto it = lhsResult.begin(); it != lhsResult.end(); ++it) 
     if (rhsResult.find(*it) == rhsResult::cend()) 
      it = lhsResult.erase(it); 
    // now we can return lhsResult because we have erased all elements from it 
    // which weren't in rhsResult 
    return lhsResult; 
} 

その他のクエリコンポーネントのサブクラスを独自の具体的な実装があります。次に、Queryには、文字列からのクエリの作成などの固有のメソッドセットがあります。純粋な仮想Query_baseがなければ、Queryという実装の詳細をすべてのコンポーネントのサブクラスと共有する必要がありました。また、これらのクラスを外部ユーザーに表示させる必要がありました。

別の「インターフェイス」Queryクラスを使用することによって、わかりやすい理解可能なリーンインターフェイスをサブシステムのユーザーに提供します。

+0

お返事ありがとうございます。私の2番目の質問は、サブシステムのユーザーにクラス階層へのアクセスを与えるサイドインターフェイスクラスを使用するのが一般的ですか? – Marron

+0

まあ、外界に晒されないような大きな「裏庭」の機械がある場合は、可能ならば、カバークラスを使うことや、おそらくカバー機能を使うのが一般的です。確かに、これはプロジェクトの設計段階を長くする可能性があります。なぜなら、クラス階層をより慎重に計画する必要があるからです。しかし、明らかなメリットがあります。例えば。 「有効な」意図されたケースのみにクラスを使用する可能性が減り、これにより実装の変更が簡単になります。 – user3159253

関連する問題