我遇到过几种情况,其中有一个包含多个对象的父对象,以及一个子对象中的信息更改会影响其他对象。将一个子对象中的更改传播到另一个子对象
例如,请考虑以下情况:
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
以上,这可能被证明是错误和乏味的。
其次,我们同样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
。
然而,缺点是这样的设计固有地导致了病态的谎言,并且违反了单一责任原则。如果程序员在ShirtStorage
上调用Remove(IShirt)
,他不会意识到从哪里删除引用。
如果程序员希望使用Mediator模式编写GUI,他将不确定发送哪条通知消息。
哪些人确切地从他们身上取下衬衫,因此需要在GUI上更新一些反映属于特定人的衬衫清单的组件?如果我有一个Catalog
班级,并且所有衬衫的名字都被删除了 - 是否不会删除对应于删除衬衫的条目(引擎盖下)?我是否也必须为我的目录更新相应的GUI组件?
第三,我们介绍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
也是如此。
但是,其缺点与第二种解决方案中的不同。最重要的是,IUniverse
实例最终成为了某种上帝对象。我需要从宇宙中移除一件衬衫的每一个地方,我都必须参考宇宙。
如果某个特定的GUI组件只是想显示ShirtStorage
的信息并允许与存储进行交互(即添加和删除衬衫),是不是会在GUI组件和Universe
之间引入一些耦合,唯一应该存在的耦合是GUI组件和IStorage
?
我写了几个应用程序使用了所有三种解决方案的组合。事实上,在不同的情况下,某些解决方案看起来比其他解决方案更好,但不一致性是一种痛苦,因为在从一种设计切换到另一种设计时,我几乎总是忘记做某些事情。