如何避免使用实现多个接口的类来破坏Liskov替换原则?

2022-09-01 00:52:08

给定以下类:

class Example implements Interface1, Interface2 {
    ...
}

当我使用实例化类时:Interface1

Interface1 example = new Example();

...然后我只能调用方法,而不能调用方法,除非我强制转换:Interface1Interface2

((Interface2) example).someInterface2Method();

当然,为了使此运行时安全,我还应该用检查来包装它:instanceof

if (example instanceof Interface2) {
    ((Interface2) example).someInterface2Method();
}

我知道我可以有一个扩展两个接口的包装器接口,但是我最终可能会得到多个接口,以满足同一类可以实现的所有可能的接口排列。所讨论的接口不会自然地相互扩展,因此继承似乎也是错误的。

当我询问运行时实例以确定其实现时,/cast 方法是否会中断 LSP?instanceof

无论我使用哪种实现,似乎都会在糟糕的设计或使用中产生一些副作用。


答案 1

我知道我可以有一个扩展两个接口的包装器接口,但是我最终可能会有多个接口来满足同一类可以实现的所有可能的接口排列。

我怀疑,如果你发现你的许多类实现了不同的接口组合,那么要么:你的具体类做得太多了;或者(不太可能)你的接口太小,太专业,以至于单独无用。

如果你有充分的理由让某些代码需要既是a又是a的东西,那么绝对可以继续制作一个扩展两者的组合版本。如果你很难为这个(不,不是)想出一个合适的名字,那么这表明你的设计是错误的。Interface1Interface2FooAndBar

绝对不要依赖铸造任何东西。它应该只用作最后的手段,通常只用于非常具体的问题(例如序列化)。

我最喜欢和最常用的设计图案是装饰图案。因此,我的大多数类只会实现一个接口(除了更通用的接口,例如 )。我想说的是,如果你的类经常/总是实现多个接口,那么这就是代码异味。Comparable


如果您正在实例化对象并在同一范围内使用它,那么您应该编写

Example example = new Example();

只是为了清楚(我不确定这是否是你的建议),在任何情况下不应该写这样的东西:

Interface1 example = new Example();
if (example instanceof Interface2) {
    ((Interface2) example).someInterface2Method();
}

答案 2

你的类可以很好地实现多个接口,并且它不会破坏任何OOP原则。相反,它遵循界面隔离原则

令人困惑的是,为什么会出现期望提供类型的东西的情况。这就是你的设计错误的地方。Interface1someInterface2Method()

以稍微不同的方式思考:想象你有另一种方法,.它不能指望也是 的一个实例。如果是这样的话,参数的类型应该不同。您展示的示例正是这样,它具有类型的变量,但期望它也是类型 。void method1(Interface1 interface1)interface1Interface2Interface1Interface2

如果希望能够同时调用这两种方法,则应将变量的类型设置为 。这样,您可以完全避免和类型转换。exampleExampleinstanceof

如果你的两个接口不是那么松散耦合,并且你经常需要从两者调用方法,那么分离接口可能不是一个好主意,或者也许你想有另一个接口来扩展两者。Interface1Interface2

通常(尽管并非总是如此),检查和类型转换通常表明一些OO设计缺陷。有时,设计会适合程序的其余部分,但是你会有一个小案例,其中类型转换比重构所有内容更简单。但是,如果可能的话,作为设计的一部分,你应该一开始就努力避免它。instanceof


推荐