设计界面

时间:2015-12-31 14:23:05

标签: c++ interface

我认为这是一个相当普遍的问题。有一个名为IService的接口,以及一些实现此接口的派生类; ServiceA,ServiceB,ServiceC和ServiceD。

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();
};

3 个答案:

答案 0 :(得分:2)

从IService派生IServiceAB,从IServiceAB派生ServiceA和ServiceB,并从IService派生ServiceD。

另一种方法是查看装饰器模式(设计模式)。

答案 1 :(得分:0)

您设计中最重要的工具是所谓的替代原则'。当您使用具有多态性的公共继承时(顺便说一句,在C ++世界中我们通常不会说'实现接口',因为接口不是C ++术语)您声称所谓的 IS-A 原则 - 也就是说,您声称对于外部观察者而言,无论出于何种目的,服务A都是IService。不多也不少。 IService中存在的所有内容都存在于ServiceA中。 ServiceA中存在的所有内容(可见外部)都存在于IService中。

每当你觉得有必要做dynamic_cast时,你显然违反了这个原则 - 因为这意味着IService不再是ServiceA,你需要专门投入它。

解。对于不遵循IS-A原则的classess,不要使用带有多态性的公共继承。在您的情况下,ServiceA IService,这意味着,IService不是ServiceA的合适基类。

答案 2 :(得分:0)

这通常是一个棘手的问题。如果我们不是服务,而是说我们处理动物 - 因为我们可以用它来代替那些可以做某些事情的东西,而不是其他东西"。假设我们有动物FishBirdCowDogCat

所以,我们想到动物一般可以做的事情:

  • 移动 - 以不同的方式,例如SwimFlyWalk
  • 发出声音 - 请拨打Talk
  • 具有长度,腿数,重量等属性
  • 有些动物可以接受培训,以便做些事情。 (狗可以取,鹦鹉可以"根据需要说话),
  • 奶牛可以生产牛奶,但(实际用途),其他都没有。

所以,我们有Fish,显然可以swim,但talk DogBird } 可以做。一个Bird可以飞,但不能Swim [是的,有优秀的游泳鸟,我简化了现实]。 Dog可以发出声音,但肯定无法飞行。 Cat可以发出声音,游泳(不情愿),但却无法Fetch

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

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

  • 对于那些不适用的功能,不执行任何操作的虚假功能(例如空功能):Talk FishSwim Bird CanSwim 1}}。
  • 条件代码 - 例如Swim函数,用于回答动物是否可以游泳。在这种情况下,如果CodeGen函数仍然被调用,它可能会抛出异常。

最终,在某个地方,我们需要知道它们是不同类型的动物,有些可以做某些事情,有些则可以做。要么让一只鸟游泳会失败(并且不应该发生),或者“不做什么”#34;。你真的必须确定正确的做法是什么。

我在编译器项目中遇到此问题。我有一个"抽象语法树"它表示解析形式的源代码。所以它有节点用于变量声明,函数声明,赋值,二进制运算,while循环,for循环等等。它们具有相同的基本接口,它提供了一个a + b函数实际上使(中间)表示) - 所以a的二进制表达式将生成加载b的代码,加载add然后将两者加在一起。函数的Codegen将为函数体等调用codegen

有六个其他通用功能适用于(几乎)所有AST条目。但是有一些操作只对特定情况才有意义,例如在处理二元运算符时,中间表示不支持字符串strcat进行连接。因此无法加载,加载和添加 - 必须调用dynamic_cast函数。对于这些特殊情况,我使用isString检查在这种情况下,类型是否与字符串类型匹配,如果是,则进入"特殊字符串操作"代码路径。我可以为所有类型添加{{1}}函数,但它会相当尴尬并且没有多大好处,因为大多数时候,它并不重要。我不认为这是一个很好的解决方案。