Использовать множественное наследование для разграничения ролей использования? - PullRequest
1 голос
/ 01 июня 2010

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

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

Обратите внимание, что class WayPointIter эквивалентно константному итератору STL, то есть точка пути не может быть изменена итератором.

class FlightPlan
{
public:
    void AppendWayPoint(const WayPointIter& at, WayPoint new_wp);
    void ReplaceWayPoint(const WayPointIter& ar, WayPoint new_wp);
    void RemoveWayPoint(WayPointIter at);

    (...)

    WayPointIter First() const;
    WayPointIter Last() const;
    WayPointIter Active() const;

    void AdvanceActiveWayPoint() const;

    (...)
};

Моя идея преодолеть эту проблему заключается в следующем: определить абстрактный интерфейсный класс для каждой роли использования и наследовать FlightPlan от обеих. Затем каждому пользователю передается только ссылка на соответствующую роль использования.

class IFlightPlanActiveWayPoint
{
public:
    WayPointIter Active() const =0;
    void AdvanceActiveWayPoint() const =0;
};

class IFlightPlanEditable
{
public:
    void AppendWayPoint(const WayPointIter& at, WayPoint new_wp);
    void ReplaceWayPoint(const WayPointIter& ar, WayPoint new_wp);
    void RemoveWayPoint(WayPointIter at);

    (...)

};

Таким образом, объявление FlightPlan необходимо будет изменить только на:

class FlightPlan : public IFlightPlanActiveWayPoint, IFlightPlanEditable
{
    (...)
};

Что вы думаете? Есть ли какие-нибудь пещерные кошки, которые я мог бы пропустить? Этот дизайн понятен, или я должен придумать что-то другое для ясности?

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

Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 01 июня 2010

С точки зрения строгого дизайна, ваша идея действительно хороша. Это эквивалентно наличию одного объекта и нескольких различных «представлений» над этим объектом.

Однако здесь есть проблема масштабирования (относящаяся к реализации). Что если у вас есть еще один объект Foo, которому нужен доступ к плану полета, вы бы добавили интерфейс IFlightPlanFoo?

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

Традиционный подход заключается в создании другого объекта, Proxy, и использовании этого объекта для адаптации / ограничения / контроля использования. Это шаблон проектирования: Proxy

Здесь вы бы создали:

class FlightPlanActiveWayPoint
{
public:
  FlightPlanActiveWayPoint(FlightPlan& fp);

  // forwarding
  void foo() { fp.foo(); }

private:
  FlightPlan& mFp;
};

Дайте ему интерфейс, который вы запланировали для IFlightPlanActiveWayPoint, создайте его со ссылкой на фактический FlightPlan объект и перенаправьте вызовы.

У этого подхода есть несколько преимуществ:

  • Зависимость: нет необходимости редактировать flightPlan.h каждый раз, когда у вас есть новое требование, поэтому нет необходимости перестраивать все приложение
  • Это быстрее, потому что больше нет виртуального вызова, и функции могут быть встроены (таким образом, почти ничего). Хотя я бы порекомендовал не вставлять их для начала (чтобы вы могли изменять их без перекомпиляции).
  • Легко добавлять проверки / журналы и т. Д. Без изменения базового класса (в случае, если у вас есть проблемы в конкретном сценарии)

Мои 2 цента.

0 голосов
/ 01 июня 2010

Не уверен насчет "пещерных кошек" ;-) но разве экипаж самолета иногда сам не меняет план полета в реальной жизни? Например. если впереди сильный шторм или аэропорт назначения недоступен из-за сильного тумана. В кризисных ситуациях капитан воздушного судна вправе принять окончательное решение. Конечно, вы можете решить не включать это в свою модель, но я подумал, что стоит упомянуть.

Альтернативой множественному наследованию может быть композиция, использующая вариацию идиома Pimpl , в которой класс-обертка не предоставляет полный интерфейс внутреннего класса. Как указывает @Matthieu, это также известно как вариант шаблона проектирования Proxy.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...