Java 继承是否与依赖倒置原则相矛盾
依赖项反转原则说(头先Java):Java 继承是否与依赖倒置原则相矛盾,java,oop,design-patterns,dependency-inversion,Java,Oop,Design Patterns,Dependency Inversion,依赖项反转原则说(头先Java): 依赖抽象。不要依赖于具体的类别 它对于继承意味着什么?作为子类依赖于具体类 我在问一个例子,比如说-有一个接口Bird(没有fly方法,因为有些鸟不能飞),它代表所有不飞的鸟。所以我创建了一个类-NonFlyingBird,它实现了Bird 现在我想为会飞的鸟上一节课。由于NonFlyingBirds和FlyingBirds具有相同的属性,我从NonFlyingBirds扩展了FlyingBirds,并实现了FlyingBirds,赋予其飞行行为 当Fly
- 依赖抽象。不要依赖于具体的类别
Bird
(没有fly方法,因为有些鸟不能飞),它代表所有不飞的鸟。所以我创建了一个类-NonFlyingBird
,它实现了Bird
现在我想为会飞的鸟上一节课。由于NonFlyingBirds
和FlyingBirds
具有相同的属性,我从NonFlyingBirds
扩展了FlyingBirds
,并实现了FlyingBirds
,赋予其飞行行为
当FlyingBirds
从一个具体的类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
实例,而不知道它们之间的区别
Bird
的接口仍然相同,或者通常应该相同,如果类确实具有不同的接口(调用代码所依赖的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
,并为所需的Bird类型传入FlightStrategy
。是的,您的直觉是正确的-继承在子级和父级之间引入了不可中断的编译时和运行时依赖关系
因此继承的应用是有限的:您应该只使用继承来实现“is”关系,而不是“has-code-to-shared”关系。但是,您应该小心:仅当对象在整个生命周期中“是”其子类时才继承。人类可以安全地扩展哺乳动物,但将学生定义为人类的一个子类是有风险的。人类可以不再是学生,你不能在运行时改变这一点
class FlyingBird extends NonFlyingBird
这是异端邪说!:)
有一种强烈的运动反对像“AbstractBird”这样的模板类。不幸的是,许多介绍性编程的书籍都教授这种代码共享模式。它本身并没有什么问题,但现在有了更好的解决方案——战略、桥梁。在Java中,您甚至可以拥有穷人的特征——接口中的默认实现
在我看来,依赖倒置原则,当应用于继承时,转化为“优先组合而非继承”。尽管我喜欢答案中的策略示例,但我也会回答,因为我认为您对依赖倒置原则有点困惑 依赖倒置原理 如果您真的不需要LinkedList行为,请不要使用此选项:
public class A {
private LinkedList<SomeClass> list;
//...
}
希望能有帮助。那些不会飞的鸟呢?它们在这个设计中是如何表现的?好的,很好的观点。我猜音乐语录不是很好的例子。。。我会简化的。但基本上,一只不会飞的鸟的飞行方法,也不会起任何作用。一只不会飞的鸟不会飞。使用空的方法有什么好处吗?我在某个地方读到,这是我需要重构的信号。这就是我提出的架构(继承)的全部要点。嗯,这要看情况而定。在我看来,空的、没有行为的方法没有任何内在的错误。在这种情况下,我觉得这是正确的行为。我认为
fly()
是一种非常合理的方法,您可以在名为Bird
的接口上找到它。我认为,当你把一只不会飞的鸟捡起来扔向空中(当然是轻轻地)时,正确的行为是不要飞。现在,每一只会飞的鸟
也是一只不会飞的鸟
,你违反了@johanneskun的规定,但我不认为用非飞鸟
替换飞鸟
会有什么问题?也许现在不会,但下个月下一个实习生会说:“我知道,我只是用instanceof
检查它是非飞鸟还是飞鸟”,这会诅咒你的决定。@johanneskun,说得好。在我看来,我需要为移动和非移动的鸟类创建两个不同的类。这是正确的吗?你有一些很好的答案,下面,但问题是错误的。当你在继承方面做了一些糟糕的事情时,这不是继承的问题。问题是你用它做了什么。别这么做。你能解释一下,‘不是所有的鸟都会飞,也不是只有鸟会飞’?@lynxx,对不起,我写得很复杂。我更新了这篇文章。注意战略和桥梁(以及所有的GoF模式)已经有30多年的历史了,所以说“现在有更好的解决方案”有点奇怪。这些解决方案已经存在了几十年。不过,我完全同意继承是一种具体的依赖关系。看到我用“是a”做了什么吗?
public class A {
private List<SomeClass> list; //or even use Collection or Iterable
//...
}
class FlyingBird extends SimpleBird implements Flyable {
void fly() {
...
}
...
}