Почему наследование сильно связано, когда композиция слабо связана с Java?

Я слышал это favor composition over inheritance снова и снова в шаблонах дизайна. некоторые из приведенных причин:

1)Inheritance is strongly coupled where as composition is loosely coupled
2) Inheritance is compile time determined where as composition is run-time
3)Inheritance breaks encapsulation where as composition does not
4) anything else I am not aware of

Было бы здорово, если бы новички, как и мне, следовали этим иллюстрациям, как наследование и композиция отличаются по отношению к вышеприведенным пунктам. Я прочитал различные ссылки SO, которые говорят о них, но прохождение примеров по этим ключевым моментам было бы замечательно для начинающих Java.

Я думаю, что очень важно понять разницу, а не просто запоминать точки.

Ответ 1

Хороший и большой вопрос для новичка, я думаю, что я должен сначала напомнить читателю, что такое наследование и состав, а затем объяснить, что именно означает Favor Composition over Inheritance.

Плюсы и минусы наследования:

Преимущества:

  • Одним из главных преимуществ динамического связывания и полиморфизма является то, что они могут помочь сделать код более легким для изменения.
  • Новая реализация проста, так как большая часть ее наследуется. Легко изменить или расширить используемую реализацию.

Недостатки:

  • Нарушает инкапсуляцию, так как она предоставляет подкласс для реализации
    детали своего суперкласса.
  • Повторное использование White-box, поскольку внутренние детали суперклассов часто видны подклассам.
  • Подклассы, возможно, придется изменить, если реализация суперкласса изменится. Реализации, унаследованные от суперклассов, не могут быть изменены во время выполнения.

О вопросе:

Наследование сильно связано там, где состав слабо связан

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

Но когда использовать и как обнаружить нам нужно наследование или состав?
Используйте наследование только тогда, когда удовлетворены все следующие критерии (правило Coad):

  1. Подкласс выражений is a special kind of а не is a role played by a.
  2. Экземпляр подкласса никогда не должен становиться объектом другого класса.
  3. Подкласс расширяет, а не отменяет или отменяет обязанности своего суперкласса.
  4. Подкласс не расширяет возможности того, что является просто служебным классом.
  5. Для класса в реальном Проблемном Домене подкласс специализирует роль, транзакцию или устройство.

Наследование - это время компиляции, которое определяется как композиция во время выполнения.

После компиляции коды базового класса будут добавлены в каждый дочерний класс.

Наследование нарушает инкапсуляцию, когда композиция не

Да. Теперь вы видите недостаток наследования.

Суть в следующем:

Убедитесь, что наследование моделирует отношения is-a. Моя основная руководящая философия заключается в том, что наследование должно использоваться только тогда, когда подкласс is-суперкласс. В приведенном выше примере Apple скорее всего, является Fruit, поэтому я склонен использовать наследование.

Важный вопрос, который нужно задать себе, когда вы думаете, что у вас есть отношение is-is, состоит в том, будет ли это отношение постоянным в течение всего жизненного цикла приложения и, к счастью, жизненного цикла кода. Например, вы можете подумать, что Employee - это Person, когда действительно Employee представляет роль, которую Person играет часть времени. Что если человек станет безработным? Что, если человек одновременно является Employee и Supervisor? Такие непостоянные отношения обычно должны моделироваться композицией.

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

Не используйте наследование только для того, чтобы достичь полиморфизма. Если все, что вам действительно нужно, это полиморфизм, но нет естественных отношений, используйте композицию с интерфейсами.

Пользуйся композицией Over Inheritance :)

Я взял это прямо из javaworld.

Ответ 2

Наследование выражается в коде с использованием расширений для конкретных классов. Когда вы его напишете, вы не сможете изменить его, не переписывая классы. Я могу изменить только путем изменения кода.

public class Foo {
    public void doSomething() { System.out.println("I'm a Foo"); }
}

public class Bar extends Foo {
    public void doSomething() { super.doSomething(); }
}

Я могу изменить только то, что Bar делает, изменяя один или оба класса.

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

public interface Foo {
    void doSomething();
}

public class FooImpl implements Foo {
    public void doSomething() { System.out.println("I'm a Foo"); }
}

public class Bar implements Foo {
    private Foo foo;

    public Bar(Foo f) { this.foo = f; }

    public void doSomething() { this.foo.doSomething(); }
}

Я могу изменить поведение Bar в этом случае просто путем передачи другой реализации интерфейса Foo.

Это один из принципов Боба Мартина SOLID: Принцип Open/Closed.