javaoopdesign-patternsdependency-inversion

Does Inheritance contradict Dependency Inversion Principle


The dependency Inversion principle says that (Head First Java):

What does it mean with respect to inheritance? As a subclass depends on concrete class.

I am asking for a case when say - There is an interface Bird (doesn't have fly method as some birds cant fly), which represents all Non-Flying Birds. So I create a class - NonFlyingBird which implements Bird.

Now I want to make a class for Birds that can fly. As NonFlyingBirds and FlyingBirds have the same attributes I extend the FlyingBirds from NonFlyingBirds and Implement Flyable to give it flying behavior.

Do Doesn't it break the dependency Inversion principle as FlyingBirds is extending from a concrete 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;
    ...
  }

Note - The only reason I am extending is because the FlyingBird has the same attributes and methods as NonFlyingBird + the flying behaviour. So it kind of makes sense to reuse code by inheritance.


Solution

  • Despite I liked an example with strategy from answers there, I will answer too, because I think you are confused a bit about dependency inversion principle.

    Dependency inversion principle means

    Don't use this, if you don't really need LinkedList behaviour:

    public class A {
        private LinkedList<SomeClass> list;
    //...
    }
    

    Use that instead:

    public class A {
        private List<SomeClass> list; //or even use Collection or Iterable
    //...
    }
    

    Dependencies are what we use inside our class.

    Inheritance

    Inheritance is what we call IS-A relationship and it has nothing to do with the principle. If you want to make class A that inherits class B, you need to answer a question: is it true that A is B. If you ask this question, you'll find that expression "FlyingBird is a NonFlyingBird" is a noncense.

    Reusing code with inheritance

    Think about this: not all birds can fly and not only birds(e.g. flies) can fly. It may lead us to an idea that we should create interface Flyable as you already done. Then we should rename NonFlyingBird to just SimpleBird, because if some creature is a bird it doesn't mean it can fly. In the end you will get:

    class FlyingBird extends SimpleBird implements Flyable { 
    
        void fly() {
            ...
        }
        ...
    }
    

    Hope it'll help.