【问题标题】:OOP composition面向对象组合
【发布时间】:2019-01-22 23:36:20
【问题描述】:

我有一个关于 OOP 组合的问题。

假设一个母亲有 0 个或多个孩子,而一个孩子只有一个亲生母亲。

为了说明这一点,我做了以下操作:

public class Mother : ObservableObject
{
    // [...]

    ObservableCollection<Child> Children {get; set;}
}

public class Child : ObservableObject
{
    public Child(Mother mother)
    {
        this.Mother = mother;

        // Adding the child to the mother's children collection
        mother.Children.Add(this);
    }

    public Mother Mother {get; set;}
}

但我想知道是否可以自动将孩子添加到母亲的收藏中,或者我是否应该使用以下内容:

Mother mother = new Mother();

Child child = new Child(mother);
mother.Children.Add(child);

谢谢:)

【问题讨论】:

  • 妈妈们不生孩子吗?
  • mother.GiveBirth(new Child()); :)
  • 也许母亲宁愿那个孩子不在她的收藏中!
  • 感谢您的回答 :) 我以母/子关系为例,事实是我希望能够从对象中检索父容器,而从父容器中检索子我'如果它误导了你,我很抱歉,但真正的场景是关于一本书和一本书的页面
  • 这实际上是对象聚合。组合指定 MotherChild 的生命周期相同,但在您的示例中,它们可以独立地被杀死。当您通过公共属性公开实例时,您必须在此处假设聚合 - 任何代码都可以获得对 Child 的引用。

标签: c# oop composition


【解决方案1】:

我更喜欢,

public class Mother : ObservableObject
{
    // ...

    public Child GiveBirth()
    {
        var newBorn = new Child(this);
        this.Children.Add(newBorn);
        return newBorn;
    }

    // ...
}

【讨论】:

  • 你快了几秒钟 :) 我同意,我在大量 Java/.NET 库和项目中看到过这种方法。
  • 我同意。把它放在构造函数中意味着一个母亲总是有一个孩子。使用专用域方法更简洁,现在母亲只有真正想要的孩子:) 但是,如果孩子是母亲存在所必需的,那么构造函数就是要走的路!
【解决方案2】:

我觉得造型有点不对劲。 MotherChild 在语义上彼此相关,但它们是同一对象的实例。他们都是Person

Person 的创建是由Person 执行的操作。所以Person 甚至不应该有一个公共构造函数,而是一个负责这个逻辑的工厂方法。像这样的:

public class Person : ObservableObject
{
    private Person()
    {
        Children = new ObservableCollection<Person>();
    }

    public Person Mother { get; private set; }
    public ObservableCollection<Person> Children { get; private set; }

    public Person Procreate()
    {
        var child = new Person();
        child.Mother = this;
        this.Children.Add(child);
        return child;
    }
}

这种建模还是有点局限,例如我们这里只讨论无性繁殖。所以我们还没有有效地模拟人类。也许我们需要添加一个父亲?

public class Person : ObservableObject
{
    private Person()
    {
        Children = new ObservableCollection<Person>();
    }

    public Person Mother { get; private set; }
    public Person Father { get; private set; }
    public ObservableCollection<Person> Children { get; private set; }

    public Person Procreate(Person father)
    {
        var child = new Person();
        child.Mother = this;
        child.Father = father;
        this.Children.Add(child);
        father.Children.Add(child);
        return child;
    }
}

我们当然会想要添加一些检查空值和诸如此类的东西。现在我们还发现我们需要指定性别。 (虽然家庭结构可能有很大差异,但创造一个人的行为已经很成熟了。)所以我们可以继续添加这样的功能。在某些时候,我们可能确实对这些进行了子类化,但这些子类最终可能主要是语义传递对象,具有此 Person 超类的硬编码默认值。

但只是为了好玩,让我们尝试添加性别...

public class Person : ObservableObject
{
    private Person(Sex gender, Person mother, Person father)
    {
        // TODO: Check for null mother and father
        this.Gender = gender;
        this.Mother = mother;
        this.Father = father;
        Children = new ObservableCollection<Person>();
    }

    public Sex Gender { get; private set; }
    public Person Mother { get; private set; }
    public Person Father { get; private set; }
    public ObservableCollection<Person> Children { get; private set; }

    public Person Procreate(Person father)
    {
        // TODO: Check for null father, confirm gender of father
        var child = new Person(PickRandomGender(), this, father);
        this.Children.Add(child);
        father.Children.Add(child);
        return child;
    }

    private Sex PickRandomGender() { /.../ }

    public enum Sex
    {
        Female,
        Male
    }
}

好的,这很有趣。通过将一些逻辑移动到构造函数也进行了一些清理。但现在还有另一个问题……父亲可以生育。这听起来有点痛苦。现在看起来我们已经准备好进行子类化了:

public class Person : ObservableObject
{
    protected Person(Sex gender, Person mother, Person father)
    {
        // TODO: Check for null mother and father
        this.Gender = gender;
        this.Mother = mother;
        this.Father = father;
        Children = new ObservableCollection<Person>();
    }

    public Sex Gender { get; private set; }
    public Person Mother { get; private set; }
    public Person Father { get; private set; }
    public ObservableCollection<Person> Children { get; private set; }

    protected Sex PickRandomGender() { /.../ }

    public enum Sex
    {
        Female,
        Male
    }
}

public class Woman : Person
{
    // TODO: Override Gender with a hard-coded value

    public Person Procreate(Person father)
    {
        // TODO: Check for null father, confirm gender of father
        var child = new Person(PickRandomGender(), this, father);
        this.Children.Add(child);
        father.Children.Add(child);
        return child;
    }
}

(我们是否也应该将Man 子类化?它在语义上看起来更清晰,但有没有女性不共享的男性特有的操作或属性?也许吧,但我们的模型还没有那么详细。)

回想起来,MotherChild 的类在这一点上似乎有点局限和短视。女人不一定是母亲,所有人都是孩子。您可以想象,有很多功能可以添加到这个系统中。但是按照同样的一般过程来构建这样的域应该可以适应这种情况。

【讨论】:

  • @J.Steen:他们呢?那些夫妇不是创造一个孩子,他们正在收养一个孩子。我们没有模拟家庭结构(还),只是创造一个孩子的身体行为。这是一个非常成熟且广为人知的操作。
  • 感谢您非常详细的回答和解释 :)
  • @J.Steen:我同意,目前领域模型相当广泛。有很多的功能要添加。哎呀,我们甚至可以谈论克隆作为一种可能的操作。但如果商业模式不支持它,那么它只是镀金。对于它的价值,我之前回答过一个类似的问题(从数据模型的角度而不是域模型的角度):stackoverflow.com/a/22114472/328193
【解决方案3】:

假设你有一个类名建筑。此类建筑有一个名为 BuildRooms() 的函数。还有另一个类 Room 具有制作房间的功能。您将 Room 类的对象创建为 r1、r2、r3 等。现在这座建筑有 3 个房间。我们可以在 Room 类中打开门和关闭门方法。建筑类由房间组成。这意味着这座建筑有3个房间。用您喜欢的任何语言编写代码。这是组合。建筑物有房间,所以它是 Has-A 关系。

    class building{

    void make_rooms()
    {
        room r1=new room(), r2=new room();
        r1.open();
        r2.close();
    }
}
class room{
    void open()
     {

     }
      void close()
      {

       }    

    }

【讨论】:

    猜你喜欢
    • 2012-06-15
    • 1970-01-01
    • 2021-05-31
    • 2021-07-30
    • 1970-01-01
    • 2020-07-17
    • 2011-04-28
    • 2011-03-27
    • 1970-01-01
    相关资源
    最近更新 更多