управление отношениями между агрегированными / составными членами класса - PullRequest
2 голосов
/ 31 мая 2009

Я создаю объекты для моделирования, используя агрегацию и композицию.

В следующем примере C ++:

class CCar
{
    CCar( CDriver* pDriver )
    { m_pDriver = pDriver; }

    CDriver* m_pDriver;

    CEngine m_Engine;

    CDriverControls m_Controls;
};

в приведенном выше примере автомобиль состоит из двигателя и набора средств управления движением (по составу). Автомобиль также должен иметь водителя (по совокупности).

Но это только объясняет иерархические отношения - водитель принадлежит автомобилю, а двигатель и органы управления также принадлежат автомобилю. Но все эти члены также связаны друг с другом - водитель должен выполнить действие на элементах управления, элементы управления должны выполнить действия на двигателе. Эти отношения также работают в нескольких направлениях - двигатель может заглохнуть и вызвать блокировку органов управления, или органы управления могут резко вращаться и причинить вред водителю? А что, если водителю не нравится звук двигателя и он покидает машину? Как работают эти отношения?

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

большое спасибо!

редактирование:

Как показывают ответы, один из способов справиться с этим - направить автомобиль к водителю, дать водителю указатель на автомобиль и т. Д. Это имеет смысл и решает этот конкретный пример. Тем не менее, с точки зрения дизайна, это увеличивает ответственность водителя, когда этому объекту поручено отслеживать, к какому автомобилю он принадлежит, но, несомненно, это обязанность контейнера отслеживать, какие объекты принадлежат друг другу? Аналогично, поручение CCar управлению этими отношениями превратит CCar в блоб. Есть ли разработанное решение для таких отношений?

Ответы [ 5 ]

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

Может быть полезно выделить интерфейсы, а не состав, агрегацию или наследование. Например, ваш класс водителя может быть написан так, чтобы он мог использовать интерфейс «рулевого колеса». Естественно, ваша реализация рулевого колеса обеспечивает реализацию интерфейса «рулевого колеса». Точно так же автомобиль предоставляет «автомобильный интерфейс», который может быть написан для реализации рулевого колеса.

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

1 голос
/ 31 мая 2009

Вопрос должен звучать так: "Моему приложению нужны эти отношения?" Например, если вы моделируете рулевое управление автомобилем для простой игры за рулем, вам, вероятно, вообще не нужно беспокоиться о моторе люка. Рулевое колесо, возможно, должно знать, что оно связано с дорожными колесами, но нет необходимости в обратной связи.

Итог - в реальном мире все связано, но в компьютерных моделях, которые мы делаем из этого мира для решения конкретных проблем, это не так.

1 голос
/ 31 мая 2009

Вы встраиваете их в методы каждого класса. То, что вы описываете, это поведение каждого класса. Ваши требования предполагают, что отношения также являются двунаправленными. Ваш класс Controls будет иметь методы, которые принимают параметр Engine и вызывают его методы. Двигатель будет иметь ограничения по оборотам, HP, крутящему моменту и т. Д., Которыми управляет контроллер, и в них встроены ограничения (например, «Если ваши обороты падают слишком низко, остановятся»).

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

0 голосов
/ 02 июня 2009

Чтобы решить этот вопрос, сначала важно установить, что делает каждый компонент.

CCar - контейнер, содержащий компоненты и агрегаты.

CDriver - объект, представляющий драйвер

CEngine - объект, представляющий двигатель

и т.д.

Для небольшой и простой программы следует использовать упрощенную конструкцию, в которой водитель получает указатель на автомобиль

CCar( CDriver* pDriver )
{
m_pDriver = pDriver;
m_pDriver->SetCar(this);
}

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

Как насчет предоставления драйверу доступа к нужным битам?

m_pDriver->SetSteeringWheel( m_SteeringWheel );
m_pDriver->SetHandBrake( m_HandBrake );

это решает эту проблему, теперь водитель не имеет доступа к другим атрибутам машины (например, цвету). Тем не менее, это дает классу CDriver больше обязанностей. Там, где CDriver может использовать множество элементов управления, класс может стать очень большим и несет ответственность за управление этими объектами рулевого колеса и ручного тормоза. Что делать, если водитель садится в другой тип автомобиля, который не имеет такого же управления, как другие? Теперь водитель должен выяснить, как управлять транспортным средством с помощью имеющихся у него органов управления? Лишняя логика Extra blob.

Решением всего этого является использование класса (или варианта) посредника для управления взаимодействием водителя с транспортным средством. Это может быть сделано одним из двух способов: водитель может иметь посредника в автомобиле, который контролирует взаимодействие водителя с автомобилем. Или у водителя может быть посредник для каждого компонента или агрегата автомобиля, с которым он должен иметь дело. Вероятно, это лучшее решение, поскольку посредники могут быть использованы для разных типов автомобилей. Посредники должны иметь возможность обрабатывать двунаправленные отношения между компонентами.

CCar, будучи контейнером, отвечает за поддержание посредников и, следовательно, за связи между его компонентами. Так и должно быть.

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

class CMediatorDriverToSteeringWheel
{
CMediatorDriverToSteeringWheel( CDriver* pDriver, CSteeringWheel* pSteeringWheel )
{
m_pDriver = pDriver;
m_pSteeringWheel = pSteeringWheel;
m_pDriver->AddMediator(this);
m_pSteeringWheel->AddMediator(this);
}
};

... 

CCar::CCar( CDriver* pDriver )
{
m_pDriver = pDriver;
new CMediatorDriverToSteeringWheel( m_pDriver, &m_SteeringWheel );
new CMediatorDriverToHandbrake( m_pDriver, &m_HandBrake );
}
0 голосов
/ 31 мая 2009

Возможно, вам нужна двусторонняя связь между автомобилем и водителем:

CCar( CDriver* pDriver ) :
     m_pDriver(pDriver)
{
    m_pDriver->SetCar(this);
}

Затем водитель может получить доступ к членам автомобиля через открытый интерфейс автомобиля.

...