6

インターフェイスへの参照/ポインタとして提示されたオブジェクトがあります。インターフェイスを変更したり、カプセル化を壊したり、恐ろしいハックを書いたりせずに、具体的なオブジェクトのメソッドが存在する場合は、そのメソッドを呼び出したいと思います。どうすればそれができますか?

例を次に示します。

私はインターフェースを持っています:

class IChatty
{
public:
    virtual ~IChatty() {};
    virtual std::string Speak() const = 0;
};

そして、このインターフェースの複数の具体的な実装:

class SimpleChatty : public IChatty
{
public:
    ~SimpleChatty() {};

    virtual std::string Speak() const override
    {
        return "hello";
    }
};

class SuperChatty : public IChatty
{
public:
    void AddToDictionary(const std::string& word)
    {
        words_.insert(word);
    }
    virtual std::string Speak() const override
    {
        std::string ret;
        for(auto w = words_.begin(); w != words_.end(); ++w )
        {
            ret += *w;
            ret += " ";
        }
        return ret;
    }
private:
    std::set<std::string> words_;
};

このメソッドは、別の新しいインターフェイスに含めることができますがSuperChatty::AddToDictionary、抽象インターフェイスには存在しません。IChatty

現実の世界では、これらのオブジェクトはファクトリを通じて構築され、それ自体が抽象的なインターフェイスの具体的なインスタンス化です。ただし、当面の問題に直交する目的のために:

int main()
{
    IChatty* chatty = new SuperChatty;
    chatty->AddToDictionary("foo");
    std::cout << chatty->Speak() << std::endl;
}

AddToDictionaryIChattyインターフェースの一部ではない (そして、その一部にすることはできない)ので、私はそれを呼び出すことができます。

カプセル化を壊したり、恐ろしいハックを書いたり、その他の設計上のショートカットを使用したりせずAddToDictionaryに、ポインターを呼び出すにはどうすればよいでしょうか?chatty

注: 現実の世界では、辞書はSuperChattyオブジェクト自体の一部であり、それから分離することはできません。

注2:具象型にダウンキャストしたくありません。

4

4 に答える 4

6

辞書を更新して参照できるオブジェクトにするSuperChatty:

class Dictionary {
public:
    void add(const std::string& word);
    const std::set<std::string>>& words() const;
    //..
};

class SuperChatty : public IChatty
{
public:
    SuperChatty(Dictionary& dictionary) :
    dictionary(dictionary) {
    }

    virtual std::string Speak() const override
    {
        auto words = dictionary.words();
        ostringstream oss;
        copy(words.begin(), words.end(),
             ostream_iterator<string>(oss, " "));
        return oss.str();
    }
};

使用法:

int main()
{   
    Dictionary dictionary;
    IChatty* chatty = new SuperChatty(dictionary);
    dictionary.add("foo");
    std::cout << chatty->Speak() << std::endl;
}

編集

さて、質問が変わりました。

これを適切に行っている場合は、基盤となる悪いシステムから自分自身を分離する必要があります。

struct Dictionary {
    virtual ~Dictionary () {}
    virtual void add(const std::string& word) = 0;
};

struct Instrumenter {
    virtual ~Instrumenter () {}
    virtual void addDictionary(Dictionary& dictionary) = 0;
};

struct Chatter {
    virtual ~Chatter() {}
    virtual string speak() const = 0;
    virtual void instrument(Instrumenter& instrumenter) = 0;
};

これらは次のように実装されます。

class BasicChatter : public Chatter {
    virtual string speak() const {
        return chatty.Speak();
    }
    virtual void instrument(Instrumenter& instrumenter) {
        // do nothing
    }
private:
    SimpleChatty chatty;
};

class SuperChatter : public Chatter {
    SuperChatter () : dictionary(chatty);

    virtual void instrument(Instrumenter& instrumenter) {
        instrumenter.addDictionary(dictionary);
    }

    virtual string speak() const {
        return chatty.Speak();
    }
private:
    SuperChatty chatty;
    DictionaryImpl dictionary;
};
于 2013-03-12T12:49:15.367 に答える
5

別のインターフェイスから派生させ、オブジェクトをそのインターフェイスにキャストできるかどうかを確認するだけです。

class IDictionary
{
public:
    virtual ~IDictionary() {};
    virtual void AddToDictionary(const std::string& word) = 0;
};

class SuperChatty : public IChatty, public IDictionary
{
     ... as before ...
};

int main()
{
    IChatty* chatty = new SuperChatty;

    IDictionary *dict = dynamic_cast<IDictionary*>(chatty);
    if (dict) dict->AddToDictionary("foo");
    std::cout << chatty->Speak() << std::endl;
}
于 2013-03-12T12:37:41.833 に答える
0

主な問題は、必要な情報を削除していることです。

したがって、主な解決策は情報を捨てないことですが、その詳細を具体化するための十分なコードが提示されていません。

第二に、技術的な解決策は、以下を使用してダウンキャストすることdynamic_castです。

IChatty* newThingy();

int main()
{
    IChatty* chatty = newThingy();
    if( SuperChatty* p_super_chatty = dynamic_cast<SuperChatty*>( chatty ) )
    {
        p_super_chatty->AddToDictionary("foo");
    }
    std::cout << chatty->Speak() << std::endl;
}

既知の静的型IChattyはポリモーフィックであるため、安全にダウンキャストできます。

于 2013-03-12T12:47:09.790 に答える
0

この特定の例では、次のようにオブジェクトを作成しない理由はありません。

SuperChatty* chatty = new SuperChatty;

chatty->AddToDictionary("foo");

chatty上記のセグメントをIChattyポインターまたは参照として渡すことができます。

void Talk(IChatty *ch)
{
   ch->Speak();
}

[同様にchattyvector<IChatty*>またはそのようなものを保管するため]。

ここでのポイントは、「新しい」インターフェース関数を使用する場合は、おそらく新しいインターフェースを持つクラスも作成する必要があるということです。

「キャストしよう」などのコードを追加すると、すぐに非常に面倒になり、エラーが発生しやすくなります。

于 2013-03-12T12:48:25.720 に答える