Вопрос: Предпочитают состав над наследованием?


Почему предпочитают композицию над наследованием? Какие компромиссы существуют для каждого подхода? Когда следует выбирать наследование над составом?


1339


источник


Ответы:


Предпочитайте композицию по наследованию, поскольку она более податлива / легко модифицируется позже, но не использует подход, основанный на compose-always. С составом легко менять поведение на лету с помощью Injection / Setters Dependency. Наследование более жесткое, так как большинство языков не позволяют вам получать более одного типа. Таким образом, гусь более или менее готовят, как только вы получаете от TypeA.

Мой кислотный тест для вышеуказанного:

  • Разве Type B хочет разоблачить полный интерфейс (все общедоступные методы не менее) типа A, чтобы тип B можно использовать там, где ожидается TypeA? Указывает наследование ,

например Биплан Cessna откроет полный интерфейс самолета, если не больше. Это делает его пригодным для получения от самолета.

  • Does TypeB хочет только часть / часть поведения, проявляемого TypeA? Указывает на необходимость Состав.

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

Обновить: Только что вернулся к моему ответу, и теперь кажется, что он неполный без конкретного упоминания о Барбаре Лисков Принцип замены Лискова как тест на «Должен ли я наследовать от этого типа?»


999



Подумайте о сдерживании как о имеет отношения. «Автомобиль» имеет «двигатель, человек» имеет «имя» и т. Д.

Думайте о наследовании как о это отношения. «Автомобиль» - это «транспортное средство, человек» - «млекопитающее и т. Д.».

Я не беру на себя ответственность за этот подход. Я взял это прямо из Второе издание кода завершено от Стив Макконнелл , Раздел 6.3. ,


345



Если вы понимаете разницу, ее легче объяснить.

Процессуальный кодекс

Примером этого является PHP без использования классов (особенно до PHP5). Вся логика кодируется в виде набора функций. Вы можете включить другие файлы, содержащие вспомогательные функции и т. Д., И вести свою бизнес-логику, передавая данные по функциям. Это может быть очень сложно управлять, поскольку приложение растет. PHP5 пытается исправить это, предложив больше объектно-ориентированного дизайна.

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

Это поощряет использование классов. Наследование является одним из трех принципов проектирования OO (наследование, полиморфизм, инкапсуляция).

class Person {
   String Title;
   String Name;
   Int Age
}

class Employee : Person {
   Int Salary;
   String Title;
}

Это наследование на работе. «Сотрудник» является «Лицом или наследуется от Лица». Все отношения наследования - это отношения «есть-а». Сотрудник также затеняет свойство Title от Person, то есть Employee.Title вернет Title для Employee, а не Person.

Состав

Композиция пользуется преимуществом над наследованием. Проще говоря, вы бы:

class Person {
   String Title;
   String Name;
   Int Age;

   public Person(String title, String name, String age) {
      this.Title = title;
      this.Name = name;
      this.Age = age;
   }

}

class Employee {
   Int Salary;
   private Person person;

   public Employee(Person p, Int salary) {
       this.person = p;
       this.Salary = salary;
   }
}

Person johnny = new Person ("Mr.", "John", 25);
Employee john = new Employee (johnny, 50000);

Композиция обычно имеет отношение «имеет» или «использует». Здесь у класса Employee есть Person. Он не наследуется от Лица, а вместо этого получает объект Person, переданный ему, поэтому он имеет «Личность».

Композиция над наследованием

Теперь скажите, что вы хотите создать тип менеджера, в результате чего вы получите:

class Manager : Person, Employee {
   ...
}

Тем не менее, этот пример будет работать отлично, если объявить личность и сотрудника Title? Должен ли Manager.Title вернуть «Менеджер операций» или «Г-н»? По составу эта двусмысленность лучше обрабатывается:

Class Manager {
   public Title;
   public Manager(Person p, Employee e)
   {
      this.Title = e.Title;
   }
}

Объект Manager состоит из Employee и Person. Поведение заголовка берется у сотрудника. Эта явная композиция устраняет двусмысленность среди других вещей, и вы столкнетесь с меньшим количеством ошибок.


172



Со всеми неоспоримыми преимуществами, обеспечиваемыми наследованием, вот некоторые из его недостатков.

Недостатки наследования:

  1. Вы не можете изменить реализацию, унаследованную от суперклассов во время выполнения (очевидно, потому что наследование определено во время компиляции).
  2. Наследование предоставляет подкласс для подробностей реализации класса его родителя, поэтому часто говорят, что наследование прерывает инкапсуляцию (в некотором смысле, вам действительно нужно сосредоточиться на интерфейсах, а не на реализации, поэтому повторное использование подкласса не всегда предпочтительнее).
  3. Тесная связь, обеспечиваемая наследованием, делает реализацию подкласса очень связанной с реализацией суперкласса, что любое изменение в родительской реализации приведет к изменению подкласса.
  4. Чрезмерное повторное использование путем подклассификации может сделать набор наследования очень глубоким и очень запутанным.

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


91



Другая, очень прагматичная причина, чтобы предпочесть состав над наследованием, связана с вашей моделью домена и сопоставлением ее с реляционной базой данных. На самом деле сложно сопоставить наследование модели SQL (в итоге вы получаете всевозможные хакерские обходные пути, например создание столбцов, которые не всегда используются, с использованием представлений и т. Д.). Некоторые ORML пытаются справиться с этим, но он всегда быстро усложняется. Композицию можно легко смоделировать с помощью отношения внешнего ключа между двумя таблицами, но наследование намного сложнее.


76



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

Вы должны отдать предпочтение картофелю по кока-коле, когда хотите есть, и кока-кола по картофелю, когда вы хотите выпить.

Создание подкласса должно означать больше, чем просто удобный способ вызова методов суперкласса. Вы должны использовать наследование, если суперкласс класса «is-a» как структурно, так и функционально, когда его можно использовать в качестве суперкласса, и вы собираетесь его использовать. Если это не так - это не наследование, а что-то другое. Композиция - это когда ваши объекты состоят из другого или имеют какое-то отношение к ним.

Поэтому для меня это похоже, если кто-то не знает, нуждается ли ему в наследстве или композиции, реальная проблема в том, что он не знает, хочет ли он пить или есть. Подумайте о своей проблемной области больше, понимайте ее лучше.


64



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

Наследование, вероятно, является наихудшей формой связи, которую вы можете иметь

Ваш базовый класс разбивает инкапсуляцию, подвергая детали реализации подклассам в виде защищенных членов. Это делает вашу систему жесткой и хрупкой. Тем не менее, более трагическим недостатком является новый подкласс, который несет с собой все багаж и мнение о цепочке наследования.

Статья, Наследование - это зло: эпическая ошибка модели данных DataNnotations Binder , просматривает пример этого в C #. Он показывает использование наследования, когда композиция должна была использоваться и как она могла быть реорганизована.


54



In Java or C#, an object cannot change its type once it has been instantiated.

So, if your object need to appear as a different object or behave differently depending on an object state or conditions, then use Composition: Refer to State and Strategy Design Patterns.

If the object need to be of the same type, then use Inheritance or implement interfaces.


36