TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2571 2.53K
День 2126. #Холивар
Композиция или наследование

Возникли у нас как-то с коллегой разногласия по поводу реализации одной функциональности. Лучшие практики объектно-ориентированного проектирования советуют нам предпочитать композицию наследованию. Но всегда ли это верно?

Коротко о домене. Есть у нас сервис поиска поставщиков электронных деталей, и сайт, на котором его можно осуществлять по подписке. А ещё есть сервис, который мы предоставляем производителям этих деталей. Заключается он в том, что мы даём им доступ к результатам нашего поиска «на их сайте». Чтобы на их сайте покупатель мог легко найти, какие поставщики их деталей есть рядом. Да, в наше время уже 99,9% читателей подумали бы, что мы предоставляем API, но нет. В смысле, API тоже есть, но поскольку сервису уже почти 20 лет и не у каждого производителя есть отдел разработки, который мог бы прикрутить этот API к их сайту, есть у нас и другая опция. Мы создаём страницы с функциональностью (поиск, результаты, возможность заказа и т.п.) и подгоняем их под внешний вид сайта производителя.

К сути. Конечно, каждый наш клиент хочет, чтобы функциональность максимально соответствовала его сайту. И стандартная реализация не всегда подходит. Кто-то хочет разную логику поиска: «с начала», «содержит», «равно». Кто-то разные регионы: по умолчанию результаты делятся на Америка, Европа и Азия, но некоторые просят поделить по-другому. И так далее, там настроек выше крыши. Большинство из них легко сохраняется в JSON, но не все. Например, кто-то хочет, чтоб на странице результатов поиска повторялась форма поиска, а кто-то, чтоб результаты определённого поставщика показывались сверху. Тут уже не избежать кастомной бизнес-логики.

Мы сошлись на том, что должен быть базовый класс со стандартным поведением, реализующий интерфейс. Вот его упрощённый код. Только методы, каждый из которых реализует нужный кусок функциональности (допустим, форматирует вывод):
puclic class SearchResults : ISearchResults
{
public Header GetHeader(…) { … }
public Rows GetRows(…) { … }
public Footer GetFooter(…) { … }
}

Кроме того, мы сошлись на том, что в случае, когда нужно что-то поменять, мы просто создаём отдельный класс для определённого производителя и переопределяем там нужную логику. Потом в DI-контейнере для каждого производителя внедряется либо класс по умолчанию, либо его специализированный (если он есть).

Так вот, моя реализация предполагала, что спец-класс просто наследовал бы от базового и переопределял нужную функциональность:
puclic class CustomSearchResults
: SearchResults, ISearchResults
{
public override Header GetHeader(…)
{
// кастомная логика
}
}

Остальные методы автоматически наследовались бы. Коллега настаивал на предпочтении композиции наследованию, и что мы должны делать декоратор базового класса. Как-то так:
puclic class CustomSearchResults 
: ISearchResults
{
private SearchResults _base;
public CustomSearchResults(SearchResults sr)
{
_base = sr;
}
public Header GetHeader(…)
{
// кастомная логика
}

public Caption GetRows(…)
=> _base.GetRows(…);
public Caption GetFooter(…)
=> _base.GetFooter(…);
}

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

Так мы к согласию и не пришли. А вы что скажете? Может есть вообще какой-то третий, «более лучший», вариант?
  • 👍 2
More from @netdeveloperdiary
  1. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  2. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  3. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  4. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  5. Oct 3, 2026Post #3360
  6. Oct 3, 2026День 2803. #Оффтоп Чем Заняться, Пока Работают Агенты? У VS Code Есть Ответ Сейчас большую…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →