设计界面
Design interfaces
我有一个我认为相当普遍的问题。有一个名为 IService
的接口,以及一些实现此接口的派生类;服务A、服务B、服务C和服务D。
ServiceA 和 ServiceB 需要一个名为 getSomeType()
的函数来公开,但其他派生类不需要此函数。此外,ServiceD 需要公开另一个其他派生类不需要的函数。我应该如何解决这个问题?我觉得使用dynamic_cast
不是正确的方法,还是吗?我还考虑创建一个新接口,以便 ServiceA 和 ServiceB 实现两个接口。
class IService
{
public:
virtual IService() {};
virtual void start() = 0;
virtual void stop() = 0;
};
class ServiceA : public IService
{
public:
void start() override;
void stop() override;
ISomeType * getSomeType();
};
IServiceAB 派生 ServiceA 和 ServiceB,从 IService 派生 ServiceD。
另一种方法是查看装饰器模式(设计模式(。
在你的设计库中,最重要的工具是所谓的"替代原则"。当你使用具有多态性的公共继承时(顺便说一句,在C++世界中,我们通常不会说"实现接口",因为接口不是一个C++术语(,你声称所谓的IS-A原则 - 也就是说,你声称对于外部观察者来说,ServiceA是,出于所有意图和目的,IService。不多也不少。IService 中存在的所有内容都存在于 ServiceA 中。存在于ServiceA中的所有东西(在外面可见(都存在于IService中。
每当你觉得有必要做一个dynamic_cast
时,你显然违反了这个原则——因为这意味着IService不再是ServiceA,你需要专门投递到它。
溶液。不要对不遵循 IS-A 原则的类使用具有多态性的公共继承。在您的情况下,ServiceA 不是 IService,这意味着 IService 不是 ServiceA 的合适基类。
这通常是一个棘手的问题。如果我们说我们与动物打交道,而不是服务——因为我们可以用它作为"可以做一些事情,但不能做其他事情的事情"的代理。假设我们有动物Fish
、Bird
、Cow
、Dog
和Cat
。
因此,我们想到动物通常可以做的事情:
- 移动 - 以不同的方式,例如
Swim
、Fly
、Walk
- 发出声音 - 称之为
Talk
- 具有长度、腿数、重量等属性。
- 有些动物可以被训练来"做事"(狗可以取,鹦鹉可以按需"说话"(,
- 奶牛可以产奶,但(出于实际目的(,其他的都不可以。
因此,我们有例如Fish
,显然,它可以swim
,但不能talk
,Dog
或Bird
可以做到。Bird
会飞,但不能Swim
[是的,有优秀的游泳鸟,我在简化现实]。Dog
可以发出声音,但肯定不会飞。Cat
可以发出声音,游泳(不情愿(,但无法Fetch
.
因此,它变成了我们如何代表这种做某些事情的能力的问题,而不是其他事情。
有几种解决方案,但最终有两种可能的解决方案
- 对那些不
- 适用的函数不执行任何操作的虚拟函数(例如空函数(:
Talk
在Fish
上,或Swim
在Bird
上。 - 条件代码 - 例如
CanSwim
函数,用于回答动物是否可以游泳。在这种情况下,如果仍在调用Swim
函数,则可能会引发异常。
某个地方,我们需要知道它们是不同类型的动物,有些可以做一些事情,有些不能。要么让鸟游泳会失败(也不应该发生(,要么"什么都不做"。你真的必须确定正确的做法是什么。
我的编译器项目中确实有这个问题。我有一个"抽象语法树",它以解析的形式表示源代码。所以它有变量声明、函数声明、赋值、二进制操作、while-loop、for 循环等节点。它们具有相同的基本接口,它提供了一个实际制作(中间表示(的CodeGen
函数 - 因此a + b
的二进制表达式将生成代码来加载a
,加载b
然后将两者相加。函数的 Codegen 将为函数体调用 codegen,依此类推。
还有六个其他通用函数适用于(几乎(所有 AST 条目。但是有一些操作仅在特定情况下才真正有意义,例如,在处理二元运算符时,中间表示不支持字符串作为串联add
。所以不能只加载、加载和添加——必须调用strcat
函数。对于这些特殊情况,我使用 dynamic_cast
来检查在这种情况下,类型是否与字符串类型匹配,如果是,则属于"特殊字符串操作"代码路径。我可以为所有类型添加一个isString
函数,但这将是相当尴尬的,而且没有太大的好处,因为大多数时候,这并不重要。我不认为这是一个很好的解决方案。
- Qt5 用户界面编译器:-i 选项不可用
- 如何使用VirtualDesktopManager界面?
- 如何使用谷歌基准测试对自定义界面进行基准测试
- 如何简化此添加界面
- 如何编译谷歌Protobuf命令行界面编译
- 动词/动作命令行界面的任何C++库?
- 在C++中模仿类似 Golang 的界面
- 记录器的流界面,C
- 如何在C++命令行界面程序中运行 COM (.ocx) 对象.(VS2017)
- 如何在不破坏装饰器模式的情况下瘦身胖界面?
- QMenuBar 不使用原生样式? c++ , QTDesigner.用户界面
- 使用部分共享界面组织对象
- 可以使用仅功能成员属性进行回调界面
- 为什么C++升压库中的累加器具有类似功能的界面?
- 界面设计:超载功能的安全性采用字符串和字符阵列
- boost :: Asio连接错误的界面
- 如何将C 中的后端写入与TCL/TCK用户界面连接
- 为什么我的自定义界面总是返回不可能的0x80040213/VFW_E_NO_CLOCK
- 是否可以通过图形用户界面 (GUI) 与 Linux 命令行界面 (CLI) 进行交互
- 指定类模板专业的界面