Sha*_*aun 2 c++ polymorphism decoupling
假设我有一个名为Component的抽象基类,它是GUI组件层次结构的根.在这种情况下,我们可能有两个子类,Button和Label,它们都是抽象类,并作为各自具体类层次结构的根存在.
从Button继承的具体类可能包括RoundButton和SquareButton.
从Label继承的具体类可能包括TextLabel和PictureLabel.
最后,假设有一个聚合Container类,它包含一组Component对象.
问题是我有指向Component对象的指针,但我需要将它们标识为Buttons或Labels.例如,如果我想指定所有Buttons的内部文本应该有一个更大的字体,我可以迭代Container中的所有Component对象,并以某种方式确定哪些是按钮,并调用一些特定于按钮的方法.
这些组件"系列"标识自己的一种方法是使用字符串.
class Component {
public:
virtual char const * const getFamilyID() const = 0;
};
// In Button.h
char const * const COMPONENT_BUTTON = "button";
class Button : public Component {
public:
virtual char const * const getFamilyID() const { return COMPONENT_BUTTON; };
};
// Code sample
if (strcmp(component->getFamilyID(),COMPONENT_BUTTON) == 0)
// It's a button!
Run Code Online (Sandbox Code Playgroud)
这是松散耦合的,因为该组件的任务是为这些家庭定义这些家庭; 它不需要了解哪些家庭存在.客户端需要知道不同的组件系列,但如果它尝试针对某些操作来定位特定的组件系列,那么这是无法避免的.
但是,假设我们有非常高的性能要求,我们希望避免比较字符串.避免将此功能设置为虚拟也很好,以便我们可以内联它.此外,如果Component的每个子类都需要声明一个全局常量,那么以某种方式修改Component类以使其成为需求或使其不必要可能会很好.
解决此问题的一种方法是在Component.h中定义一个枚举器
enum COMPONENT_FAMILY {
COMPONENT_BUTTON = 0,
COMPONENT_LABEL,
// etc...
};
Run Code Online (Sandbox Code Playgroud)
在这种情况下,getFamilyID()只能返回COMPONENT_FAMILY枚举,我们基本上只能比较整数.不幸的是,这意味着任何新的组件系列都必须在此枚举中"注册",这很容易但对其他程序员来说并不完全直观.此外,该方法仍然必须是虚拟的,除非我们创建一个我们知道将具有极低基数(非理想)的非静态COMPONENT_FAMILY成员.
什么是解决这个问题的好方法?在我的情况下,性能是关键,虽然类似于枚举解决方案似乎很容易,但我想知道我是否忽略了更好的方法.
---编辑---
我意识到我应该指出,在实际系统中,Container等价物只能存储每个族的1个组件.因此,组件实际上存储在地图中,例如:
std:map<COMPONENT_FAMILY, Component*>
Run Code Online (Sandbox Code Playgroud)
应用于我的简单示例,这意味着Container只能包含1个Button,1个Label等.
这使得检查特定类型的组件(日志时间)的存在变得非常容易.因此,这个问题更多的是关于如何表示COMPONENT_FAMILY,以及在我将它添加到地图时如何确定Component的类型.
换句话说,组件的唯一目的是将其标识为添加到Container的特定功能,并且所有组件一起定义容器的特定行为.
所以,我不需要知道组件的类型,这已经暗示了.我特别要求Container提供特定类型的Component.我需要的是一种方式让Component传达它的类型,这样它就可以在第一时间进行映射.
我需要将它们标识为按钮或标签.
那是你的问题.这种假设虽然很常见,但通常是错误的.
通过在构造时为控件分配UI策略,您可以绕过"需求"来了解其具体类型.在基类的绘制时(或每当)检索该UI策略的字体属性:
class IVisualStrategy
{
...
virtual const Font& GetFont() const = 0;
...
};
class HeavyVisuals : public IVisualStrategy
{
Font font_;
...
HeavyVisuals() : font_(15, Bold) {}
virtual const Font& GetFont() const { return font_; }
...
};
class LightVisuals : public IVisualStrategy
{
Font font_;
...
LightVisuals() : font_(12, Regular) {}
virtual const Font& GetFont() const { return font_; }
...
};
Run Code Online (Sandbox Code Playgroud)
从基地检索:
class Control
{
...
private:
void OnPaintOrSomething()
{
DrawTextWithFont(GetVisualStrategy().GetFont());
}
virtual const IVisualStrategy& GetVisualStrategy() const = 0;
};
class Button : public Control
{
HeavyVisualStrategy visualStrategy_;
...
private:
virtual const IVisualStrategy& GetVisualStrategy() const
{
return visualStrategy_;
}
}
class Label : public Control
{
LightVisualStrategy visualStrategy_;
...
private:
virtual const IVisualStrategy& GetVisualStrategy() const
{
return visualStrategy_;
}
}
Run Code Online (Sandbox Code Playgroud)
更灵活的设计是在具体的控件类和构造函数中保持对IVisualStrategy的共享指针 - 注入它,而不是将它们硬设置为Heavy或Light.
此外,要在此设计中的对象之间共享字体,可以应用Flyweight模式.