2010-11-19 5 views
5

私は、これらのクラスを知らなくても、他のクラスから情報を取得する必要があるC++モジュールを持っています。明白なアプローチはインターフェイスを使用することです。多くの仮想メソッドを持つ1つのインターフェイスを持っていますか?または、仮想メソッドが1つしかない多くのインターフェイスを使用していますか?

例を挙げておきます。すべての図書に独自の特性と機能があり、図書の特性を実現したり、機能を実行させたりするためには、図書にインターフェースを実装する必要があります。このように:

class Library 
    { 
    public: 
     void addBook(IBook &book); 
    }; 

class IBook 
    { 
    public: 
     string getAuthor() = 0; 
     string getTitle()  = 0; 
     string getISBNCode() = 0; 
     size_t getNofPages() = 0; 
     size_t getNofImages() = 0; 
     double getPrice()  = 0; 
     void printBook() = 0; 
     void convertToPdf() = 0; 
    }; 

残念ながら、残念ながら、すべての種類の書籍にこれらのすべての方法を実装することは意味がありません。

  • いくつかの書籍がそう、
  • 一部の本はいくつかの本を購入することはできませんISBNコード
  • を持っていない(私はgetNofImagesは()を実装する必要はありません)の画像を持っていません私は1つのだけのインタフェースを持っているので、彼らはいくつかの本はいくつかの本をPDF

に変換することはできません

  • を印刷することはできません
  • 価格を持っていない、私はのためにすべてを実装することを強制していますすべての本と0を返し、 ""を返すか、または無関係なら実装に何もしません。

    代替は、このように、多くのインターフェイスでこれらのインタフェースを分割することができます

    class IBook 
        { 
        public: 
         string getAuthor() = 0; 
         string getTitle()  = 0; 
         size_t getNofPages() = 0; 
        }; 
    
    class IISBNGetter 
        { 
        public: 
         string getISBNCode() = 0; 
        }; 
    
    class IImagesGetter 
        { 
        public: 
         size_t getNofImages() = 0; 
        }; 
    
    class IBuyable 
        { 
        public: 
         double getPrice()  = 0; 
        }; 
    
    class IPrintable 
        { 
        public: 
         void printBook() = 0; 
        }; 
    
    class IConvertible 
        { 
        public: 
         void convertToPdf() = 0; 
        }; 
    

    ブッククラスは、彼らだけが本当にサポートするインターフェイスを実装する必要があります。異なるインタフェースを持つことの利点は、それが何をサポートしているライブラリのための明確であるということであり、それが何かを呼び出すことのリスクを持っていることはありません

    bookid = myLibrary->addBook (myBook); 
    myLibrary->setISBNGetter (bookid, myBook); 
    myLibrary->setImageGetter (bookid, myBook); 
    myLibrary->setBuyable  (bookid, myBook); 
    

    :図書館に本を追加する

    は、このようなものになりそれは単にサポートされていません。

    しかし、すべての本は特性/機能の可能な組み合わせを持つことができるので、私は1つの方法で多くのインターフェースを完成させます。

    インターフェイスを整理してこのようなものを得るためのより良い方法はありませんか?

    ラムダ式の使用についても考えていましたが、画面の背後でこれは1つの方法で多くのインターフェイスを持つこととほとんど同じです。

    アイデア?

  • 答えて

    8

    私はすべてのメソッドを実装するためにのiBookを持っていると思います:

    class IBook 
        { 
        public: 
         virtual ~IBook() {} 
    
         virtual string getAuthor() { return ""; } // or some other meaningful default value 
         virtual string getTitle() { return ""; } 
         virtual string getISBNCode() { return ""; } 
         virtual size_t getNofPages() { return 0; } 
         virtual size_t getNofImages() { return 0; } 
         virtual double getPrice() { return .0; } 
         virtual void printBook() {} 
         virtual void convertToPdf() {} 
        }; 
    

    あなたの選択肢は私にとってあまりにも乱雑であることから、あなたは混乱のインターフェイスの多くで終わると思います。それ以外の場合は、Visitor patternがここに適用できるかどうかを確認することができます。

    2

    解決策は、基本インターフェイスを純粋な仮想メソッドで保つことですが、実際の実装では、中間メソッドクラスから継承して、仮想メソッドの既定の実装を提供することができます。

    この中間クラスの一般的な実装の1つは、各メソッドにある種の "MethodNotImplemented"例外をスローすることで、クラスのユーザーはケースバイケースでそれらをキャッチできます。

    非存在メソッドの呼び出しが「例外的」でないような場合には例外的な例外がありますが、これらのメソッドを空にしたりデフォルト値を返すアプローチもあります。

    2

    私は極端なものになる必要はありませんが、中間の方法を選択すると思います。 1つのインターフェイスを持つことは良くありません。一方、Interface Segregation Principle (ISP)は多くのインターフェイスを持つことでコードが損なわれます。私は1つのコアIBookを残し、残りの部分を考えます。例えば、IPrintableとIConvertible(pdf変換用)は1つのインターフェースに入るかもしれません。おそらくIBuyableとIISBNGetterは別のものになります。

    1

    もう1つの解決策は、インターフェイスを保持することですが、空にできる戻り値にはboost :: optionalを使用することです。

    class IBook 
        { 
        public: 
         virtual ~Ibook(){} 
    
         virtual string getAuthor() = 0; 
         virtual string getTitle()  = 0; 
         virtual string getISBNCode() = 0; 
         virtual size_t getNofPages() = 0; 
         virtual size_t getNofImages() = 0; 
         virtual boost::optional<double> getPrice()  = 0; // some have no price 
         virtual void printBook() = 0; 
         virtual void convertToPdf() = 0; 
        }; 
    
    2

    私はあなたが実際に ISBNを持つ、と(この場合は、iBookの中で)ISBNを照会インタフェースを実装するの区別を引くべきだと思います。

    「本」の定義の一部として、「ある書籍が「それがISBNを持っているかどうかを確認することができますか?」とは言えません。

    "none"を示す空の値を返すのが嫌なら、それは十分です。一部のドメインでは、空の文字列が有効な値であるため、それは可能ではありません。あなたは持っている可能性があり:

    bool hasISBNcode(); 
    string getISBNcode(); 
    

    か:

    std::pair<bool, string> getISBNcode(); 
    

    または類似します。

    IBookのいずれかの関数を呼び出す前に、dynamic_castと一緒にいると、2^nの異なる具体的なクラスがあります。あるいは、あなたのサンプルコードでは、本にISBNがあるかどうかのビジネスにライブラリを関与させます(これは私には間違っているようです - 図書館とは何の関係もなく、単なる本の所有物です)。それらのどれもは特に楽しく働くことができず、ここでは必要ないようです。

    これらの種類のものが必要と思われる場合は、おそらく戦略を使用できます。どのように検索が行われるかを知っているブッククラスなしで、ConcreteBookと定義して、何らかのヘルパーオブジェクトを使用してISBNを検索してください()。次に、特定の本に実際に1つまたは複数のオブジェクトがあるかどうかに応じて、さまざまなオブジェクトを挿入して、それを行います。しかし、おそらくデータベースのどこかにあるNULL値可能な列のために、ちょっと残酷に思えます。

    +0

    は完全に同意し、本が自然に潜在的にISBNや価格を有していると考えられています。 ISBNがあり、そのISBN値が何であるかは、購入可能かどうか、また価格は何かを質問したい特定の本があるとすれば、それらの操作は意味的に 'IBook'インターフェースに属します。異なる視点から、 'ISBNGetter'インターフェースを持つことは、異なるオブジェクトがISBNを持つかもしれないが、本だけがそれを持っているように思えます。 'IBook'ではないオブジェクトに対して余分なインターフェースを使用する状況はありません。 –

    +0

    @dribeas:はい、 'IISBNGetter'が' IBook'から派生しているのは、余分なインターフェースを持っていても価値があるかもしれません。私はISBN-13はUPCと互換性があると思う。もしそうなら、 'IUPCGetter'はより一般的で、' IBuyable'は確かにです。 'IBuyable'は、すべての本がそれを実装しているかどうか、または一部しか実装していないかどうかにかかわらず、価値のあるインターフェースになるかもしれません。 –

    1

    std::map<std::string, IBase>のように、基本インターフェイスのシングルトンオブジェクトへのポインタのコンテナを各ブックに含めることができます。次に、名前でインターフェイスを要求し、ポインタを取得して(またはnull)、doDefault()を呼び出します(または、必要ならばIDerivedへのポインタをアップキャストします)。各インタフェース関数は、最初の(または唯一の)パラメータとしてBookへのポインタを持つ必要があります:doDefault(const Book*)

    1

    2わずかに(ほんの少し!)関連の問題があります

    • 論理部品階層の適切な抽象化。
    • ヌル値の可能性。

    他は、それぞれの問題の解決策を提供しています。すなわち、最初のものについては、すべてのインタフェースを使用しないでください.1つのインタフェースごとにシングルメソッドを使用するのではなく、そこにある階層をモデル化してみてください。後者に関しては、boost::optionalがあり、データアイテムの存在に関する別のクエリメソッドが追加されている可能性があります。

    私はこれを書いているので、現在の回答から明らかではないかもしれないが、実際には2つの別々の問題であることを強調したい。

    スタイル(明快さの別の側面)については、これは何ですか?getSin Javaism stuff?

    x = 2*getSin(v)/computeCos(v)

    だけsinを書き、C++で意味がありません。 :-)

    乾杯は& HTH。、

    関連する問題