面向对象的设计父/子关系

时间:2010-10-06 16:59:49

标签: c# oop

这是关于创建与对象的父/子关系的一般最佳实践问题。

假设我有Wheel和Car对象,我想将一个Wheel对象添加到car对象

public class Car{

    private List<Wheel> wheels = new List<Wheel>();

    void AddWheel ( Wheel WheelToAdd)
        {
            wheels.Add(WheelToAdd)
            //Some Other logic relating to adding wheels here
        }
    }
}

到目前为止一切顺利。但是,如果我想让我的车轮的Car属性说出它与哪辆母车有什么关系呢?像这样的东西

 public class Wheel {

     private Car parentCar;
     public Car 
     {
        get
        {
         return parentCar
        }

  }

}

将车轮添加到汽车时,您可以在此时设置车轮的父属性?您可以在Car.AddWheel方法中设置它,但是Wheel对象的Car属性必须是读/写,然后您可以在AddWheel方法之外设置它,从而产生不一致。

任何想法,非常感谢提前

8 个答案:

答案 0 :(得分:9)

更好的设计方法(域驱动设计)指定您应首先确定这些实体的域模型要求是什么......并非所有实体都需要独立访问,如果Wheel属于此类别,它的每个实例将始终是Car对象的子成员,您不需要在其上放置父属性... Car成为所谓的根实体,访问Wheel的唯一方法是通过Car对象。

即使Wheel对象需要独立访问,域模型要求也应该告诉您使用模式需要什么。任何Wheel是否会作为单独的对象传递,而不是它的父级?在这些情况下,Car父母是否相关?如果父Car 的标识与某些功能相关,那么为什么不简单地将完整的复合Car对象传递给该方法或模块?包含的复合对象(如Wheel)必须自己传递的情况,但父级的身份(它所属的对象)是必需的和/或相关的,实际上并不是常见的情况,使用上述类型的分析来接近您的设计可以避免您向系统添加不必要的代码。

答案 1 :(得分:4)

双向关系往往很难正确实施。这里的主流建议应该是“不要这样做,很可能你实际上并不需要它,而且它会给你带来更多弊大于利。”

如果经过仔细考虑后,您决定双向关系是合理的,那么您可以设置Car属性internal的设置者,它不能完全防止设置不需要的值的盗贼,但它确实显着限制了表面积。

答案 2 :(得分:2)

如果该设置为空,您可能需要考虑设置一个仅设置Wheel.parentCar的setter,但前提是您能够假设您的第一个设置有效且因此可以忽略任何其他尝试

编辑:但是,是的,这将是添加Car对象的正确位置。您还可以进行检查以创建(例如)Wheel.validateCar(Car carInQuestion),其中强制parentCar属性仅设置在Wheel中当前Car对象所在的位置。这意味着您将有一个公共方法,可以根据轮子的特定实例搜索Car.wheels成员资格。但只有当你真的觉得需要严格时才会这样做。

答案 3 :(得分:0)

对于我来说,Wheel对象的Car属性是可写的是有意义的,因为没有什么可以阻止你将Wheel从一个ToyotaCamry(Car的子类)移动到另一个。虽然您可能希望在原车上有一个Drivable属性,但在允许调用Car.Drive()之前检查List.Count是否为4。

你用汽车制造车轮吗?如果这样设置了当时的财产。否则在Wheel连接到Car时设置它。

答案 4 :(得分:0)

问题:Wheel实例能否及时分配给不同的Car实例?

1)如果是,则公开一个公共方法来设置parentCar。您可以使其成为一种流畅的方法,以方便代码:

public class Wheel 
{
    private Car parentCar;

    public ParentCar 
    {
        get
        {
            return parentCar;
        }
    }

    public void SetParentCar(Car _parentCar)
    {
        parentCar = _parentCar;
        return this;
    }
}

在指定的地方添加方向盘,进行分配:

void AddWheel ( Wheel WheelToAdd)
{
    wheels.Add(WheelToAdd.SetParentCar(this));
    //Some Other logic relating to adding wheels here
}



2)如果没有,只需在构造函数中设置父级。

public class Wheel 
{
    private Car parentCar;

    public ParentCar 
    {
        get
        {
            return parentCar;
        }
    }

    public Wheel(Car _parentCar)
    {
        parentCar = _parentCar;
    }
}

答案 5 :(得分:0)

我要选择“不要”。轮子不仅仅是Car的属性。可以在TrailerCartFerrisWheel上使用滚轮(好吧,也许这有点延伸)。关键是,通过使用车轮本身的车轮属性,您可以将车轮的设计与车辆上的车辆结合使用,此时,您将失去对车辆的任何可重复使用性。

在这种情况下,通过让方向盘知道它实际使用的内容,似乎有很多损失而且收获甚少。

答案 6 :(得分:0)

答案 7 :(得分:0)

可能是中心的一点点,但从概念上讲,车轮是汽车的“一部分”。你考虑过使用部分课吗?乘客是队列的“一部分”。我发现在某些情况下使用部分类和定义良好的接口非常有用。您可以将整个对象的各个方面抽象为接口,并将每个接口实现为部分类。结果是一个完整的对象,可以通过其部分接口进行抽象,以满足一系列合同场景。