6

我有一个对象作为接口的引用/指针。如果该方法存在,我想在具体对象上调用该方法,而无需更改接口、破坏封装或编写任何可怕的 hack。怎么做到呢?

这是一个例子。

我有一个界面:

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;
}

由于AddToDictionary不是IChatty接口的一部分(也不能成为接口的一部分),所以我可以调用它。

如何AddToDictionarychatty不破坏封装、编写一些可怕的 hack 或采用任何其他设计快捷方式的情况下调用指针?

注意:在现实世界中,字典是SuperChatty对象本身的一部分,不能与它分开。

NOTE2:我不想贬低具体类型。

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();
}

[同样用于存储chattyavector<IChatty*>或类似的东西]。

我的意思是,如果您要使用“新”接口函数,那么您可能还想创建具有新接口的类。

将代码添加到“尝试强制转换”等会很快变得非常混乱,并且容易出错。

于 2013-03-12T12:48:25.720 回答