【问题标题】:managing relationships between aggregated / composited members of a class管理类的聚合/组合成员之间的关系
【发布时间】:2010-10-30 06:23:46
【问题描述】:

我正在使用聚合和组合为模拟创建实体。

在以下 C++ 示例中:

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

    CDriver* m_pDriver;

    CEngine m_Engine;

    CDriverControls m_Controls;
};

在上面的示例中,汽车由发动机和一组驾驶控制装置(按组合)组成。一辆车还必须有一个司机(通过聚合)。

但这只是解释了层次关系——司机属于汽车,引擎和控制也属于汽车。但是这些成员也都相互关联——驾驶员必须对控件执行操作,控件必须对引擎执行操作。这些关系也可以在多个方向上起作用——发动机可能会熄火并导致控制装置失灵,或者控制装置可能会疯狂旋转并伤害驾驶员?如果驾驶员不喜欢发动机的声音并离开汽车怎么办?这些关系如何运作?

我正在从经常与其他对象交互的许多不同对象中合成许多不同的实体,并且对如何以设计的方式管理这些关系感兴趣。

谢谢!

编辑:

正如回复所建议的那样,管理此问题的一种方法是将汽车指向驾驶员,并为驾驶员提供指向汽车的指针等。这很有意义并解决了这个特定示例。然而,在设计意义上,这增加了驾驶员的责任,这个对象的任务是跟踪它属于哪辆车,但这肯定是容器的责任来跟踪哪些对象属于一起?同样,让 CCar 负责管理这些关系会将 CCar 变成一个 blob。是否有处理此类关系的设计解决方案?

【问题讨论】:

    标签: c++ relationship aggregation composition


    【解决方案1】:

    您将它们构建到每个类的方法中。您所描述的是每个班级的行为。您的要求表明关系也是双向的。您的 Controls 类将具有采用 Engine 参数并调用其方法的方法。引擎将对其 RPM、HP、扭矩等进行限制,由 Control 控制,这些限制将内置于其中(例如,“如果您的 RPM 降得太低,则熄火”)。

    这不仅仅是作曲。您将行为和规则构建到方法中。这些方法可能采用表达您需要的参数。

    【讨论】:

      【解决方案2】:

      问题应该是“我的应用程序需要这种关系吗?”例如,如果您正在为一个简单的驾驶游戏模拟驾驶汽车,您可能根本不需要担心天窗的电机。方向盘可能需要知道它与车轮相连,但不需要反向关系。

      底线 - 在现实世界中,一切都是相互关联的,但在我们为解决特定问题而构建的该世界的计算机模型中,它们并非如此。

      【讨论】:

      • 感谢您的回复。给出的示例实际上只是一个示例(尽管可能可以使用更好的示例!),但我试图说明我面临的一个问题。我实际上并不是在写一个关于汽车和司机的程序。
      【解决方案3】:

      强调接口而不是组合、聚合或继承可能很有用。例如,可以编写您的驱动程序类,使其可以使用“方向盘”接口。自然,您的方向盘实现提供了“方向盘”接口的实现。同样,汽车提供了一个“汽车接口”,可以编写方向盘实现以利用该接口。

      您的实现可以使用组合、聚合和继承。但在这种方法中,真正驱动设计的是接口。无论您在给定实例中使用组合、聚合还是继承,都只是实现细节。

      【讨论】:

        【解决方案4】:

        您可能希望在汽车和驾驶员之间建立双向关联:

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

        那么驱动就可以通过Car的公共接口访问Car的成员了。

        【讨论】:

          【解决方案5】:

          要解决这个问题,首先要确定每个组件的作用。

          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 可以使用很多控件,类可以变得非常大,并负责操作这些方向盘和手刹对象。如果驾驶员乘坐的车辆类型与其他车辆不同,该怎么办?现在驾驶员必须弄清楚如何使用其拥有的控件来操作车辆?额外的逻辑。额外的斑点。

          解决所有这些问题的方法是使用中介类(或变体)来控制驾驶员与车辆的交互方式。这可以通过以下两种方式之一来完成,驾驶员可以拥有汽车的中介,它控制驾驶员与汽车的交互方式。或者,驾驶员可以为必须处理的汽车的每个组件或集合设置一个调解器。这可能是一个更好的解决方案,因为调解器可以重复用于不同类型的汽车。中介必须能够处理组件之间的双向关系。

          作为容器的 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 );
          }
          

          【讨论】:

          • 感谢所有其他答案,你们都做出了巨大的贡献,帮助我思考我的问题,但似乎没有一个答案能真正涵盖整个问题。我继续研究,发现这是最相关的答案。我对其他解决方案仍然持开放态度(我相信有很多),所以如果您认为自己有更好的解决方案,请提供更多答案!
          猜你喜欢
          • 1970-01-01
          • 2015-03-28
          • 2013-11-05
          • 1970-01-01
          • 1970-01-01
          • 2021-09-09
          • 1970-01-01
          • 1970-01-01
          • 2019-06-01
          相关资源
          最近更新 更多