我有一个对象作为接口的引用/指针。如果存在该方法,我想在具体对象上调用一个方法,而不需要更改接口,破坏封装或编写任何可怕的黑客攻击。怎么办呢?
这是一个例子。
我有一个界面:
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
界面的一部分(不能成为其中的一部分),我可以称之为。
如何在AddToDictionary
指针上调用chatty
而不破坏封装,编写一些可怕的黑客,或采用任何其他设计快捷方式?
注意:在现实世界中,字典是SuperChatty
对象本身的一部分,不能与它分开。
注意2:我不想向下转换为具体类型。
答案 0 :(得分: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;
};
答案 1 :(得分: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;
}
答案 2 :(得分:0)
主要问题是您正在丢弃所需的信息。
因此,主要的解决方案是不丢弃信息,但没有足够的代码来充实细节。
其次,使用dynamic_cast
来解决一个简单的kludge解决方案:
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
是多态的。
答案 3 :(得分:0)
对于这个特定的例子,没有理由不像这样创建对象:
SuperChatty* chatty = new SuperChatty;
chatty->AddToDictionary("foo");
您仍然可以将上述细分中的chatty
作为IChatty
指针或参考传递,例如
void Talk(IChatty *ch)
{
ch->Speak();
}
[同样用于将chatty
存储在vector<IChatty*>
或类似的内容中。
我的观点是,如果您要使用“新”接口函数,那么您可能还想创建具有新接口的类。
添加代码以“尝试投射”等等,很快就会变得非常混乱,并且容易出错。