设计界面

Design interfaces

本文关键字:界面      更新时间:2023-10-16

我有一个我认为相当普遍的问题。有一个名为 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();
};
从 IService 派生 IServiceAB,从

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 的合适基类。

这通常是一个棘手的问题。如果我们说我们与动物打交道,而不是服务——因为我们可以用它作为"可以做一些事情,但不能做其他事情的事情"的代理。假设我们有动物FishBirdCowDogCat

因此,我们想到动物通常可以做的事情:

  • 移动 - 以不同的方式,例如 SwimFlyWalk
  • 发出声音 - 称之为Talk
  • 具有长度、腿数、重量等属性。
  • 有些动物可以被训练来"做事"(狗可以取,鹦鹉可以按需"说话"(,
  • 奶牛可以产奶,但(出于实际目的(,其他的都不可以。

因此,我们有例如Fish,显然,它可以swim,但不能talkDogBird可以做到。Bird会飞,但不能Swim[是的,有优秀的游泳鸟,我在简化现实]。Dog可以发出声音,但肯定不会飞。Cat可以发出声音,游泳(不情愿(,但无法Fetch.

因此,它变成了我们如何代表这种做某些事情的能力的问题,而不是其他事情。

有几种解决方案,但最终有两种可能的解决方案

    对那些不
  • 适用的函数不执行任何操作的虚拟函数(例如空函数(:TalkFish 上,或SwimBird上。
  • 条件代码 - 例如CanSwim函数,用于回答动物是否可以游泳。在这种情况下,如果仍在调用 Swim 函数,则可能会引发异常。
最终,在

某个地方,我们需要知道它们是不同类型的动物,有些可以做一些事情,有些不能。要么让鸟游泳会失败(也不应该发生(,要么"什么都不做"。你真的必须确定正确的做法是什么。

我的编译器项目中确实有这个问题。我有一个"抽象语法树",它以解析的形式表示源代码。所以它有变量声明、函数声明、赋值、二进制操作、while-loop、for 循环等节点。它们具有相同的基本接口,它提供了一个实际制作(中间表示(的CodeGen函数 - 因此a + b的二进制表达式将生成代码来加载a,加载b然后将两者相加。函数的 Codegen 将为函数体调用 codegen,依此类推。

还有六个其他通用函数适用于(几乎(所有 AST 条目。但是有一些操作仅在特定情况下才真正有意义,例如,在处理二元运算符时,中间表示不支持字符串作为串联add。所以不能只加载、加载和添加——必须调用strcat函数。对于这些特殊情况,我使用 dynamic_cast 来检查在这种情况下,类型是否与字符串类型匹配,如果是,则属于"特殊字符串操作"代码路径。我可以为所有类型添加一个isString函数,但这将是相当尴尬的,而且没有太大的好处,因为大多数时候,这并不重要。我不认为这是一个很好的解决方案。