继承是否与依赖倒置原则相矛盾

Does Inheritance contradict Dependency Inversion Principle

依赖倒置原则说(Head First Java):

关于继承是什么意思?作为子class取决于具体class .

我问的是一个案例,当说 - 有一个接口 Bird(没有 fly 方法,因为有些鸟不能飞),它代表所有非飞鸟。所以我创建了一个 class - NonFlyingBird 来实现 Bird

现在我想为会飞的小鸟制作一个class。由于 NonFlyingBirdsFlyingBirds 具有相同的属性,我从 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 接口的

类 仍然可以传递 FlyingBirdNotFlyingBird 实例,而不知道其中的区别。

如果 类 确实有不同的接口(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() {
        ...
    }
    ...
}

希望对您有所帮助。