【发布时间】:2015-01-29 05:51:44
【问题描述】:
假设我的项目使用了一个库LibFoo,它通过多个类提供其功能,例如FooA 和FooB。现在,有许多类似的库(例如,提供BarA 和BarB 的LibBar)提供与LibFoo 相同的功能,我希望我的项目的用户能够选择使用哪个库,最好在运行时。
为此,我创建了一个“包装层”,它定义了我期望从库中获得的接口。在我的示例中,该层包含两个接口:IfaceA 和 IfaceB。然后,对于我想要支持的每个库,我创建一个“实现层”,使用其中一个库来实现接口。
我现在的问题在于如何很好地实现实现层。为了演示我的问题,考虑我们有以下接口(用 C++ 显示,但应该适用于类似的语言):
class IfaceA
{
public:
virtual void doSomethingWith(IfaceB& b) = 0;
...
};
class IfaceB
{
...
};
LibFoo 的实现层中的类将持有来自LibFoo 对应类的对象。接口中的操作应该使用这些对象来实现。因此(请原谅我的可怕名字):
class ConcreteAForFoo : public IfaceA
{
public:
void doSomethingWith(IfaceB& b) override
{
// This should be implemented using FooA::doSomethingWith(FooB&)
}
private:
FooA a;
};
class ConcreteBForFoo : public IfaceB
{
public:
FooB& getFooB() const
{
return b;
}
private:
FooB b;
};
问题在于实现ConcreteAForFoo::doSomethingWith:它的参数类型为IfaceB&,但我需要访问ConcreteBForFoo 的实现细节才能正确实现该方法。我发现这样做的唯一方法是到处使用丑陋的向下转型:
void doSomethingWith(IfaceB& b) override
{
assert(dynamic_cast<ConcreteBForFoo*>(&b) != nullptr);
a.doSomethingWith(static_cast<ConcreteBForFoo&>(b).getFooB());
}
由于不得不向下转换通常被认为是代码异味,我不禁想到应该有更好的方法来做到这一点。还是我一开始就设计错了?
TL;DR
给定一层相互依赖的接口(其中一个接口中的方法接收对其他接口的引用)。这些接口的实现如何在不向下转换或在接口中暴露这些细节的情况下共享实现细节?
【问题讨论】:
-
但我需要访问 ConcreteBForFoo 的实现细节才能正确实现该方法 如果是这种情况,您是否可以考虑重写 IfaceB 以便您不要不需要知道实现细节?
-
@stijn:我当然可以考虑这一点,但如果不在接口中公开来自
LibFoo的类,我看不出有什么办法。 -
我希望回答(4)downcasts 是好的也是可以的。如果不是,有什么理由不呢? :)
-
如果两个这样的库同时处于活动状态,并且您交叉传递实例(即,将库 1 中的 a b 传递到库 2 中的 a?)会发生什么?
-
这不是 COM 基本上试图成为的样子吗?