我遇到过几种情况,其中我有一个包含多个对象的父对象,以及一个子对象中信息的更改会影响其他对象。
例如,请考虑以下情况:
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组件?
第三,我们将Catalog
和Add(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
?
我已经编写了几个使用了所有三种解决方案的应用程序。事实上,在不同的情况下,某些解决方案似乎比其他解决方案更好,但是不一致是一种痛苦,因为当从一种设计切换到另一种设计时,我几乎总是忘记做某些事情。
答案 0 :(得分:5)
每次我都会听到“#34;传播变化”这句话。在oop的背景下,我立即想到Observer pattern。因此,不要将您的域对象的责任赋予&#34;同步&#34;添加和删除衬衫,我会介绍另一个接管此职责的类,并让您的域对象引发相关事件。而且你也一直坚持单一责任原则。
HTH