我应该如何设计一组只支持某些操作的相关类? 我正在用C++编写一个基于幻灯片的应用程序。 每张幻灯片都有一个幻灯片项目集合,可以 包括标题、按钮、矩形等项目
只有部分项目支持填充,而其他项目支持填充 不要 在这种情况下,实现幻灯片项目填充的最佳方法是什么? 以下是我想到的两种方法:我应该如何设计一组只支持某些操作的相关类? 我正在用C++编写一个基于幻灯片的应用程序。 每张幻灯片都有一个幻灯片项目集合,可以 包括标题、按钮、矩形等项目,c++,design-patterns,C++,Design Patterns,只有部分项目支持填充,而其他项目支持填充 不要 在这种情况下,实现幻灯片项目填充的最佳方法是什么? 以下是我想到的两种方法: 创建一个可填充的界面,并为幻灯片项目实现此界面 它支持填充,将所有与填充相关的属性保留在接口中。在迭代幻灯片项目列表时,dynamic_将其强制转换 进入可填充的中,如果成功,请执行与填充相关的操作 制作一个填充类。将填充指针作为幻灯片项目类的一部分,分配 fill对象,指向那些支持fill的对象的fill指针,其余对象将其保持为空。给出一个函数GetFill,该函数将返
中,如果成功,请执行与填充相关的操作
填充类。将填充
指针作为幻灯片项目类的一部分,分配
fill
对象,指向那些支持fill的对象的fill
指针,其余对象将其保持为空。给出一个函数GetFill
,该函数将返回项目的fill
(如果存在),否则返回NULL
最好的方法是什么?我对性能和可维护性感兴趣。创建基类幻灯片项目:
class SlideItem {
public:
virtual ~SlideItem();
virtual void fill() = 0;
};
然后为无法填充的对象执行空实现:
class Button : public SlideItem {
public:
void fill() { }
};
以及其他项目的适当填充实施:
class Rectangle : public SlideItem {
public:
void fill() { /* ... fill stuff ... */ }
};
然后把它们都放在一个容器里。。如果你想填补空缺就给每个人打电话。。。易于维护。。谁在乎业绩:)
如果您真的需要快速代码,那么您的第一个解决方案肯定是好的。但是,如果你这样做,确保你不必每次都投下它,你想填补。一次性将指针扔到一个可填充的容器中。然后,如果必须填充,则迭代此可填充容器
再说一次,你在这方面投入了太多的精力,却没有获得合理的性能增益。(当然我不知道你的申请,这可能是合理的……但通常不是)我会将两者结合起来。使您的
Fillable
接口成为GetFill
方法的返回类型。这比动态强制转换方法要好。使用dynamic cast查询接口需要实际的幻灯片项目对象实现接口(如果要支持该接口)。但是,使用诸如GetFill
之类的访问器方法,您可以选择提供指向实现该接口的其他对象的引用/指针。如果接口实际上是由该对象实现的,那么也可以返回this
。这种灵活性有助于避免类膨胀,并促进创建可由多个类共享的可重用组件对象
编辑:
这种方法也可以很好地处理空对象模式。对于不支持
Fillable
的对象,您可以返回一个实现该接口的简单无操作对象,而不是返回空指针。这样,您就不必担心总是检查客户机代码中的空指针了。看起来您要查找的内容接近实际情况。你的#2接近这种模式。下面是我要做的:
补课。使填充指针成为幻灯片项目类的一部分,将填充对象指定为仅支持填充的对象的填充指针,其余对象将其保持为空。创建一个函数GetCapability(Capability.Fill),该函数将返回项目的填充(如果存在),否则返回NULL。如果某些对象已经实现了可填充接口,则可以将对象转换为可填充指针 考虑存储变量项,例如boost::Variant
您可以定义一个boost::variant
(如果您拥有所有权,您应该使用智能指针),然后有一个要迭代的变量列表。答案取决于此
如果不是每个对象都应该处理填充,那么我不认为必须用fill/get\u fillable\u instance/…
来混乱基本接口有什么意义。然而,你可以仅仅通过
struct slide_object
{
virtual void fill() {} // default is to do nothing
};
但这取决于您是否认为幻灯片对象抽象类中应该出现fill
。但是,除非不可填充是例外情况,否则它很少应该这样做
如果需要提供两个不同的对象类(且不超过两个),则动态强制转换是正确的,其中一些对象是可填充的,另一个与可填充性无关。在这种情况下,有两个子层次结构并在需要的地方使用动态转换是有意义的
我在某些情况下成功地使用了这种方法,只要分派逻辑不分散(即,只有一个或两个地方可以进行动态强制转换),这种方法简单且可维护
如果您希望有更多类似填充的行为,那么dynamic\u cast
是一个错误的选择,因为它将导致
if (auto* p = dynamic_cast<fillable*>(x))
...
else if (auto* p = dynamic_cast<quxable*>(x))
...
if(自动*p=dynamic_cast(x))
...
else if(自动*p=动态_cast(x))
...
这很糟糕。如果需要,那么实现一个访问者模式。我建议对形状使用一个界面,并使用一个返回填充符的方法。例如:
class IFiller {
public:
virtual void Fill() = 0;
protected:
IFiller() {}
virtual ~IFiller() {}
};
class IShape {
public:
virtual IFiller* GetFiller() = 0;
protected:
IShape() {}
virtual ~IShape() {}
};
class NullFiller : public IFiller {
public:
void Fill() { /* Do nothing */ }
};
class Text : public IShape {
public:
IFiller* GetFiller() { return new NullFiller(); }
};
class Rectangle;
class RectangleFiller : public IFiller {
public:
RectangleFiller(Rectangle* rectangle) { _rectangle = rectangle; }
~RectangleFiller() {}
void Fill() { /* Fill rectangle space */ }
private:
Rectangle* _rectangle;
};
class Rectangle : IShape {
public:
IFiller* GetFiller() { return new RectangleFiller(this); }
};
我发现这个方法更易于维护和扩展,但不会带来主要的性能问题。这称为fat接口,是一种反模式。@Armen Tsirunyan:好的,对。如果有一个反模式,那么也必须有相应的模式。您当然可以向我解释:)哦,如果您的解决方案包含的类(适配器或任何东西)数量是原来的两倍,我对此不感兴趣。@duedl0r:恐怕我看错了整个段落,对不起。不幸的是,您又一次拥有了一个庞大的基类。。。这是很不幸的。你的胖班变成了变种。。。我不喜欢这种方法,尤其是如果类填充接口的数量预计会增加的话。我特别想说的是