“可选接口”可能不是标准术语,所以让我举一个例子。假设我有:
interface Car {
start();
honk();
}
现在,我可以使用HondaCar
,PriusCar
等实现。好极了!但是,如果鸣喇叭对我或我的用户来说并不是那么重要,那么我决定做这样的事情:
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
。但是,这确实会对用户代码施加更多责任来管理所有这些。
所以,我列出了一些我看到的优点和缺点,但我很好奇其他人的想法。哪种情况更可取?
答案 0 :(得分:3)
构图而不是继承,我们的主题在这里,是一个重要的OOP原则。它告诉我们通过它们的函数定义我们的对象。这意味着,您的第二种方法是最佳实践。这样做:
public class SomeCar: ICar, IHonk {}
public Interface ICar {}
public Interface IHonk {}
设计能力而不是身份。
答案 1 :(得分:2)
我认为有两种不同的界面
如果您想要鸣喇叭,请实施界面
答案 2 :(得分:0)
正如其他人所提到的,单独的接口是更好的解决方案。值得注意的是,它符合Interface Segregation Principle的SOLID。
但是,另一种方法是使用功能容器:
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();
}
这当然可以有很多语法变化,具体取决于语言和所需的语义。