Противоречит ли наследование принципу инверсии зависимости? - PullRequest
4 голосов
/ 27 сентября 2019

Принцип инверсии зависимости гласит, что (Head First Java):

  • Зависит от абстракций.Не зависит от конкретных классов.

Что это означает в отношении наследования? Поскольку подкласс зависит от конкретного класса.

Я спрашиваю случай, когда говорят: «Есть интерфейс Bird (не имеет метода полета, поскольку некоторые птицы не могут летать), который представляет все не летающие птицы.Поэтому я создаю класс - NonFlyingBird, который реализует Bird.

Теперь я хочу создать класс для птиц, которые могут летать.Как NonFlyingBirds и FlyingBirds имеют те же атрибуты, я продлить FlyingBirds от NonFlyingBirds и реализовать Flyable дать ему летающий поведение.

Не нарушает ли это принцип инверсии зависимостей, поскольку 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 + летное поведение.Поэтому имеет смысл повторно использовать код по наследству.

Ответы [ 3 ]

2 голосов
/ 27 сентября 2019

Да, ваша интуиция верна - наследование вводит неразрывную зависимость времени компиляции и времени выполнения между дочерним элементом и его родителем.

Поэтому применение наследования ограничено: для реализации следует использовать только наследование.отношение «есть», а не «имеет код для совместного использования».Вы должны быть осторожны: наследуйте только тогда, когда объект «является» его подклассом в течение всей жизни.Человек может безопасно расширять Млекопитающее, но рискованно определять Студента как подкласс Человека.Люди могут перестать быть Студентами, и вы не можете изменить это во время выполнения.

class FlyingBird extends NonFlyingBird

Это ересь!:)

Существует сильное движение против шаблонных классов, таких как "AbstractBird".К сожалению, многие вводные книги по программированию учат этому шаблону совместного использования кода.В этом нет ничего плохого, но сейчас есть лучшие решения - Стратегия, Мост.В Java вы даже можете иметь черты бедняка - реализации по умолчанию в интерфейсах.

На мой взгляд, принцип инверсии зависимости применительно к наследованию переводится как "композиция Favour по наследованию".

2 голосов
/ 27 сентября 2019

Короткий ответ: Нет.

Более длинный ответ: используйте шаблон стратегии.

Классы, которые зависят от интерфейса 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 по умолчанию и передайте FlightStrategy для нужного вам типа птицы.

1 голос
/ 27 сентября 2019

Несмотря на то, что мне понравился пример со стратегией из ответов, я тоже отвечу, потому что я думаю, что вы немного запутались в принципе инверсии зависимостей.

Принцип инверсии зависимостей означает

Дон 'Используйте это, если вам действительно не нужно поведение LinkedList:

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

Используйте это вместо этого:

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

Зависимости - это то, что мы используем внутри нашего класса.

Наследование

Наследование - это то, что мы называем отношениями IS-A, и оно не имеет ничего общего с принципом.Если вы хотите создать класс A, который наследует класс B, вам нужно ответить на вопрос: верно ли, что A является B. Если вы зададите этот вопрос, вы обнаружите, что выражение "FlyingBird является NonFlyingBird"бессмысленность.

Повторное использование кода с наследованием

Подумайте об этом: не все птицы могут летать и не только птицы (например, мухи) могут летать.Это может привести нас к мысли, что мы должны создать интерфейс Flyable, как вы уже сделали.Затем мы должны переименовать NonFlyingBird в SimpleBird, потому что если какое-то существо является птицей, это не значит, что оно может летать.В конце вы получите:

class FlyingBird extends SimpleBird implements Flyable { 

    void fly() {
        ...
    }
    ...
}

Надеюсь, это поможет.

...