继承是否与依赖倒置原则相矛盾
Does Inheritance contradict Dependency Inversion Principle
依赖倒置原则说(Head First Java):
- 依赖于抽象。不要依赖于具体的 classes.
关于继承是什么意思?作为子class取决于具体class .
我问的是一个案例,当说 - 有一个接口 Bird
(没有 fly 方法,因为有些鸟不能飞),它代表所有非飞鸟。所以我创建了一个 class - NonFlyingBird
来实现 Bird
。
现在我想为会飞的小鸟制作一个class。由于 NonFlyingBirds
和 FlyingBirds
具有相同的属性,我从 NonFlyingBirds
扩展了 FlyingBirds
并实现了 Flyable
以赋予它飞行行为。
Do 它不破坏依赖倒置原则因为 FlyingBirds
是从具体的 class NonFlyingBirds
扩展而来的吗?
interface Bird { // Represents non Flying birds
void getColor();
void getHeight();
...
}
class NonFlyingBird implements Bird {
void getColor();
void getHeight();
...
}
class FlyingBird extends NonFlyingBird implements Flyable { // Does it break Dependency Inversion principle by extending concrete NonFlyingBird class?
Flyable fly;
...
}
注意 - 我扩展的唯一原因是 FlyingBird
具有与 NonFlyingBird
+ 飞行行为相同的属性和方法。所以通过继承来重用代码是有意义的。
简答:否
更长的答案:使用策略模式。
依赖于Bird
接口的 类 仍然可以传递 FlyingBird
或 NotFlyingBird
实例,而不知道其中的区别。
如果 类 确实有不同的接口(FlyingBird
中的新方法,您的调用代码取决于),那就有问题了。
或许解决您的问题的更好方法是使用策略模式。
即兴表演示例:
public interface Bird {
void fly();
}
public class BirdImpl implements Bird {
private FlightStrategy flightStrategy;
public BirdImpl(FlightStrategy flightStrategy) {
this.flightStrategy = flightStrategy;
}
public void fly() {
this.flightStrategy.fly();
}
}
public interface FlightStrategy {
void fly();
}
public class FlyingBirdFlightStrategy implements FlightStrategy {
public void fly() {
System.out.println("Wings flap");
System.out.println("Wings flap");
System.out.println("Wings flap");
System.out.println("Wings flap");
}
}
public class NonFlyingBirdFlightStrategy implements FlightStrategy {
public void fly() {
// do nothing non flying birds can't fly.
}
}
然后在创建要使用的 Bird
时,创建默认的 BirdImpl
并传入 FlightStrategy
作为您想要的鸟的类型。
是的,您的直觉是正确的 - 继承在子项与其父项之间引入不可破坏的编译时和 运行 时依赖性。
因此继承的应用是有限的:只能使用继承来实现"is"关系,而不是"has code to be shared"关系。但是,您应该小心:仅当对象 "is" 在其整个生命周期中都是其子类时才继承。 Human 可以安全地扩展 Mammal,但是将 Student 定义为 Human 的子类是有风险的。人类可以不再是学生,你无法在 运行 时间内改变这一点。
class FlyingBird extends NonFlyingBird
这是异端邪说! :)
反对模板 类(如 "AbstractBird")的运动很激烈。不幸的是,许多介绍性编程书籍都教导了这种代码共享模式。它本身并没有什么问题,但现在有更好的解决方案 - 策略、桥牌。在 Java 中,您甚至可以拥有穷人的特征 - 接口中的默认实现。
在我看来,依赖倒置原则在应用于继承时转化为 "Favour composition over inheritance"。
尽管我喜欢那里答案中的策略示例,但我也会回答,因为我认为您对依赖倒置原则有点困惑。
依赖倒置原则的意思
如果您真的不需要 LinkedList 行为,请不要使用它:
public class A {
private LinkedList<SomeClass> list;
//...
}
改用它:
public class A {
private List<SomeClass> list; //or even use Collection or Iterable
//...
}
依赖项是我们在 class.
中使用的继承
继承就是我们所说的IS-A关系,与原理无关。如果你想让classA继承classB,你需要回答一个问题:A是B是真的吗。如果你问这个问题,你会发现那个表达式“FlyingBird
是一个 NonFlyingBird
" 是一个废话。
通过继承重用代码
想一想:不是所有的鸟都能飞,也不是只有鸟(例如苍蝇)会飞。
这可能会让我们产生一个想法,即我们应该像您已经完成的那样创建 Flyable 接口。然后我们应该将 NonFlyingBird
重命名为 SimpleBird
,因为如果某种生物是鸟,并不意味着它会飞。最后你会得到:
class FlyingBird extends SimpleBird implements Flyable {
void fly() {
...
}
...
}
希望对您有所帮助。