将一个子对象的更改传播到另一个子对象

时间:2015-09-19 02:29:40

标签: oop design-patterns

我遇到过几种情况,其中我有一个包含多个对象的父对象,以及一个子对象中信息的更改会影响其他对象。

例如,请考虑以下情况:

interface IUniverse
{
    IStorage ShirtStorage;
    IList<IHuman> Humans;
}

Interface IStorage:
{
    string Location;
    int Capacity;
    IList<IShirt> Items;
}

Interface IHuman
{
    string Name;
    int Age;
    IList<IShirt> Shirts;
}

我想在我的宇宙中从ShirtStorage中移除一件特定的衬衫,但与此同时,因为衬衫已经从生存中移除,所以它也应该从所有人身上移除。

我想到了3种方法:

<小时/> 首先,我们可以将Add(IClothing)Remove(IClothing)方法引入IStorage<T>IHuman

interface IUniverse
{
    IStorage ShirtStorage;
    IList<IHuman> Humans;
}

Interface IStorage
{
    string Location;
    int Capacity;
    IList<IShirt> Items;
    **void Add(IShirt);**
    **void Remove(IShirt);**
}

Interface IHuman
{
    string Name;
    int Age;
    IList<IShirt> Shirts;
    **void AddShirts(IShirt);**
    **void RemoveShirts(IShirts);**
}

之后,上面接口的实现将没有任何内容,当从ShirtStorage中删除时,它将从所有人身上移除特定的衬衫。

这种设计的缺点是,每当程序员从宇宙中移除衬衫时,他将不得不手动移除每个人的每一个参考。

也就是说,程序员必须知道宇宙的确切结构才能移除单件衬衫。如果宇宙的结构变得非常复杂,那么对特定衬衫的引用可能不仅仅出现在IHuman中,这可能被证明是错误和乏味的。

其次,我们同样将<{1}}和Add(IShirt)方法引入接口 Remove(IShirt)IStorage

IHuman

..但是这一次,我们使用上述接口的实现,以便在引擎盖下进行一些通知。也就是说,

interface IUniverse
{
    IStorage<IShirt> ShirtStorage;
    IList<IHuman> Humans;
}

Interface IStorage
{
    string Location;
    int Capacity;
    IList<IShirt> Items;
    **void Add(IShirt);**
    **void Remove(IShirt);**
}

Interface IHuman
{
    string Name;
    int Age;
    IList<ICShirt> Shirts;
    **void AddShirt(IShirt);**
    **void RemoveShirt(IShirt);**
}

实际上,通过将所有通知放在接口的实现中,当接口的消费者想要将其从存在中移除时,它不必经历对特定class Storage : IStorage { IUniverse parentUniverse; string Location; int Capacity; IList<IShirt> Items; // ... Implementation for Add(IShirt item) is trivial void Remove(IShirt item) { this.Items.Add(item); foreach (IHuman human in this.parentUniverse) foreach(IClothing clothing in human.Clothings) if (clothing == item) human.RemoveClothing(clothing); } } 的每个可能的引用,从而使得在这个意义上,与先前的解决方案相比,它IShirt

然而,缺点是这种设计固有地导致病态说谎,并且也违反了单一责任原则。如果程序员在better上调用Remove(IShirt),他就不会知道从哪里删除了哪些引用。

如果所述程序员希望使用Mediator模式编写GUI,他将不确定要发送哪个通知消息。

哪些人准确地从他们身上移除了衬衫,因此需要在GUI上更新某些组件,这些组件反映了属于特定人类的衬衫列表?如果我有一个ShirtStorage课程,其中包含所有衬衫的名称,那么与移除的衬衫相对应的条目也不会被移除(在引擎盖下)?我是否还必须更新目录的相应GUI组件?

第三,我们将CatalogAdd(IShirt)方法引入Remove(IShirt)代替:

IUniverse

通过这样做,我们强制界面的消费者接受删除衬衫不仅会影响衬衫存储,还会影响interface IUniverse { IStorage ShirtStorage; IList<IHuman> Humans; void Add(IShirt); void Remove(IShirt); } Interface IStorage: { string Location; int Capacity; IList<IShirt> Items; } Interface IHuman { string Name; int Age; IList<IShirt> Shirts; } 的其他成员。

然而,缺点与第二种解决方案中的缺点相同。最重要的是,IUniverse个实例最终变成了一个上帝对象。在我需要从宇宙中移除衬衫的每个地方,我都必须参考宇宙。

如果特定的GUI组件只是想显示IUniverse的信息并允许与存储进行交互(即添加和删除衬衫),那么这不会引入GUI组件之间的某种耦合和ShirtStorage,当唯一应该存在的耦合是GUI组件和Universe

我已经编写了几个使用了所有三种解决方案的应用程序。事实上,在不同的情况下,某些解决方案似乎比其他解决方案更好,但是不一致是一种痛苦,因为当从一种设计切换到另一种设计时,我几乎总是忘记做某些事情。

1 个答案:

答案 0 :(得分:5)

每次我都会听到“#34;传播变化”这句话。在oop的背景下,我立即想到Observer pattern。因此,不要将您的域对象的责任赋予&#34;同步&#34;添加和删​​除衬衫,我会介绍另一个接管此职责的类,并让您的域对象引发相关事件。而且你也一直坚持单一责任原则。

HTH