Каковы преимущества/недостатки Опции 2 более чем 1 в этом примере?
Опция 1 (Наследование):
public class SalesList : List<Sales>
{
//methods that add extra behavior List<Sales>
}
Опция 2 (Состав):
public class SalesList
{
private List<Sales> _list;
//methods that add extra behavior to List<Sales>
}
Вариант 1
- Преимущества - все преимущества наследования и повторного использования
- Недостатки - код теперь сигнализирует потребителям, что он получен из List. Если это потребуется изменить позже, это затронет всех связанных потребителей.
Вариант 2
- Преимущества - детали реализации, касающиеся хранения данных о продажах, абстрагируются от потребителя. Таким образом, если необходимо изменить реализацию (например, словарь), потребитель класса будет невосприимчив к этим изменениям.
- Недостатки - теперь класс SalesList должен будет предоставлять дополнительные методы для получения и / или установки внутреннего объекта _list. это дополнительный код, который необходимо поддерживать. Кроме того, если внутренняя реализация изменится, вам нужно позаботиться о том, чтобы по-прежнему поддерживать предыдущее поведение ...
Всего несколько мыслей, которые приходят на ум.
HTH.
Преимущество варианта 2 - гибкость. Используя композицию, вы можете выбрать, какие части интерфейса списка вы открываете.
Представьте, что позже вы решите больше не переходить из другого класса. К настоящему времени некоторые пользователи вашей библиотеки уже использовали методы, предоставляемые List, поэтому вам необходимо реализовать все методы списка самостоятельно: add / addAll / contains / keepAll. Это множество методов, которые нужно реализовать для простого списка продаж.
По сути, это сводится к тому, что «когда сомневаешься, оставь это в стороне», о чем Джошуа Блох говорит в Bumper-Sticker API Design . Каждый аспект API должен быть как можно меньше, но не меньше. Вы всегда можете добавить вещи позже, но вы не можете их убрать.
Основным недостатком Composition является то, что вам нужно обернуть (дублировать) все общедоступные методы частного списка, если вам нужно представить один и тот же интерфейс, в наследовании у вас все они уже доступны, но вы не можете переопределить любой из них, который был сделан непереопределяемым (в C # это означает, что метод должен быть помечен как «виртуальный», в Java он не может быть помечен как «окончательный»).
В C # 3 и выше вы можете использовать методы расширения, которые создают видимость наследования, не нарушая по-настоящему дерево иерархии.