Як я можу уникати запису багато пропускних функцій у обгортку?


13

У мене є клас, який обгортає інший клас загального базового типу. Оскільки інтерфейс базового типу досить великий, це передбачає написання безлічі функцій проходження. Я шукаю спосіб уникнути цього.

Давайте зробимо приклад:

      Car
     /   \
Volvo     VolvoWithTrailer

Тепер я маю реалізувати кожну функцію в автомобільному інтерфейсі для VolvoWithTrailer і викликати відповідну функцію на обгорнутому об’єкті Volvo, за винятком, можливо, GetMaxSpeed ​​(), який поверне щось нижче. В основному у мене буде багато функцій на кшталт

int VolvoWithTrailer::GetNumSeats() {
  return mVolvo.GetNumSeats()
}

Очевидним способом вирішити це було б зробити VolvoWithTrailer підкласом Volvo

    Car
     |
   Volvo
     |
VolvoWithTrailer

але це, здається, порушує принцип переваги композиції над спадщиною.

Як інакше я можу уникати написання всіх цих обгортків (мова - C ++)? Або - якщо це ваша позиція - чому я повинен просто писати їх / просто використовувати спадщину? Чи є якась магія шаблонів, яка могла б допомогти у цьому?


2
Не хоч це надто багато, і думати на Java. Що з інтерфейсом "Причепа"? VolvoWithTrailer розширить Volvo і реалізує інтерфейс Trailer?

7
Вигідний склад над спадщиною лише тоді, коли це має сенс зробити.
Роберт Харві

@RobertHarvey: +1. Це настанова, а не "принцип" і, звичайно, не абсолютна заповідь.
Мейсон Уілер

У Python ви можете вирішити це за допомогою самоаналізу (те, що C # називає рефлексією).
user16764

1
Чи не обробляє ваш IDE генерація коду?
Емі Бланкенсіп

Відповіді:


5

На мою думку, ви повинні писати все, що вам потрібно написати, щоб покращити довгострокову стабільність та ремонтопридатність програми. Який сенс уникнути написання 20 рядків коду сьогодні, якщо кожен раз, коли ви торкаєтесь чогось, що використовує цей код, або повертаєтесь для підтримки цього класу, це коштує вам додаткових 5 хвилин або більше, тому що ви цього часу не вкладали?

Тож питання стає тоді "що вам потрібно написати?" Я не думаю, що ви надаєте достатньо інформації, щоб можна було дати остаточну відповідь, тому я просто перелічу деякі речі, про які я би подумав, приймаючи рішення:

1. Чи потрібно мати об'єкт Трейлер сам, зараз або в осяжному майбутньому?
Якщо так, у вас є досить хороший аргумент, щоб зробити обгортку навколо обох складених об’єктів, оскільки вам вже доведеться обернути те чи інше. Так само може бути послідовним.

2. Чи є якийсь із трьох типів (автомобіль, причіп, CarWithTrailer) у зоні коду, яку розробники часто вживають або схоже, що це стане?
Якщо так, будьте дуже обережні, оскільки наслідки вибору неправильної речі можуть бути дуже дорогими амортизацією щоразу, коли код торкається. Якщо ні, то, що ви вирішите , це не має ніякого значення . Просто виберіть напрямок і вирушайте з ним.

3. Що найлегше зрозуміти?
Чи скаже вам один підхід, щоб хтось, що прийшов за вами, негайно «дістав» те, що ви намагаєтесь зробити? Чи є у ваших товаришів по команді особливі упередження, які можуть ускладнити один підхід? Якщо всі речі рівні, виберіть рішення, яке накладає найменше когнітивне навантаження.

4. Чи є додаткові переваги щодо використання одного підходу над іншим?
Одне, що вискакує на мене, - це те, що ідея обгортки має явну перевагу, якщо ви пишете, що ваш CarWithTrailerWrapper може загортати будь-який автомобіль та будь-який причіп у CarWithTrailer. Потім ви закінчуєте писати всі свої прохідні методи, але ви пишете їх лише один раз , а не один раз для кожного класу автомобілів.

Це робить ваші початкові інвестиції в написання методів проходження набагато дешевшими, оскільки ви додаєте більше автомобілів та причепів. Це також допомагає зменшити спокусу прив’язати речі надто безпосередньо до Volvo або VolvoWithTrailer, зменшивши при цьому наслідки приєднання до CarWithTrailerWrapper, оскільки ви можете набагато більше досягти лише однією реалізацією.

Можливо, є чіткі переваги, які ви отримаєте безкоштовно, використовуючи метод розширення, але я їх не бачу.

Гаразд, так виглядає, що я говорив про те, щоб продовжувати і заповнювати методи проходження для нетривіальних додатків.

Я не програміст на C ++, тому я не маю уявлення, які інструменти для генерації коду доступні вам. IDE, який я використовую, може генерувати методи з інтерфейсу, і я можу додати шаблони коду, які б зробили написання проходів досить безболісними. Якщо у вас немає доступу до IDE, що робить це, ви можете фактично дістатись далеко, дозволяючи Excel писати код для вас .


3

Оскільки інтерфейс базового типу досить великий, це передбачає написання безлічі функцій проходження.

І там є ваша проблема.

Інтерфейси повинні відповідати лише одній відповідальності. Тримання їх тонкими та зосередженими обмежує кількість найпростіших робіт, які вам можуть знадобитися у випадку декоратора, проксі-сервера чи іншої обгортки (на додаток до того, щоб компроміс класу використовувати, тестувати, підтримувати та розширювати).


5
Ага, що? Один клас може реалізовувати кілька інтерфейсів, а інтерфейси можуть успадковувати один від одного. Тож навіть якщо інтерфейс досить малий, це не має нічого спільного з тим, чи потрібно Класам, які їх реалізують, чимало функцій чи прохідних функцій. Чим менше і більш чітко визначені класи , тим більш імовірно, буде , що ви будете потрібні наскрізна функцій.
Емі Бланкенсіп
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.