如何处理“可选接口”?

时间:2018-04-17 02:34:08

标签: oop interface

“可选接口”可能不是标准术语,所以让我举一个例子。假设我有:

interface Car {
  start();
  honk();
}

现在,我可以使用HondaCarPriusCar等实现。好极了!但是,如果鸣喇叭对我或我的用户来说并不是那么重要,那么我决定做这样的事情:

interface Car {
  start();
  canHonk(); // return true if honking is supported
  honk(); // undefined behavior of canHonk is false
}

所以这就是我所谓的“可选接口”,因为实际上支持honk是可选的。它看起来仍然是一个很好的,定义良好的界面,但另一种表达方式是将它分成两个界面:

interface Car {
  start();
}

interface Honkable {
  honk();
}

现在,如果用户代码确实需要进行一些鸣喇叭,则必须将其传递给Honkable。如果它是可选的,它可以采用空指针。如果它根本不关心鸣喇叭,它可以完全忽略Honkable。但是,这确实会对用户代码施加更多责任来管理所有这些。

所以,我列出了一些我看到的优点和缺点,但我很好奇其他人的想法。哪种情况更可取?

3 个答案:

答案 0 :(得分:3)

构图而不是继承,我们的主题在这里,是一个重要的OOP原则。它告诉我们通过它们的函数定义我们的对象。这意味着,您的第二种方法是最佳实践。这样做:

public class SomeCar: ICar, IHonk {}

public Interface ICar {}

public Interface IHonk {}
  

设计能力而不是身份。

答案 1 :(得分:2)

我认为有两种不同的界面

如果您想要鸣喇叭,请实施界面

答案 2 :(得分:0)

正如其他人所提到的,单独的接口是更好的解决方案。值得注意的是,它符合Interface Segregation PrincipleSOLID

但是,另一种方法是使用功能容器:

public class FeatureContainer {
    // ...

    public bool isAvailable<T>() {
    // ...
    }

    public T getFeatureOrNull<T>() {
    // ...
    }
}

然后有例如:

public abstract class Car : FeatureContainer {
// ...
};

public class SomeCar : Car {
    public SomeCar()
    : base(/* instantiate all implementations of supported interfaces */)
    {}
}

所以你可以:

Car aCar = getSomeCar();

if (aCar.isAvailable<Honkable>()) {
    Honkable h = aCar.getFeatureOrNull<Honkable>();
    h.honk();
}

这当然可以有很多语法变化,具体取决于语言和所需的语义。