叶在综合模式实现了Component接口,包括Add
, Remove
和GetChild
方法是叶是永远不会使用。 这似乎是违反接口隔离原则的。
所以是组合模式的使用SOLID ?
链接组合模式: http://www.dofactory.com/Patterns/PatternComposite.aspx
叶在综合模式实现了Component接口,包括Add
, Remove
和GetChild
方法是叶是永远不会使用。 这似乎是违反接口隔离原则的。
所以是组合模式的使用SOLID ?
链接组合模式: http://www.dofactory.com/Patterns/PatternComposite.aspx
在为您的链接和大多数书籍中描述的模式真正的气味是Component
具有的方法Composite
。 我想这大概是因为该模式是相当老,并一直重复这样多年。 我的观点是,只有Composite
要有相关的合成的任何方法。
我曾经转换棋盘游戏到电脑游戏。 棋子放在地图地球的,划分为六边形上。 所有的六边形的99%表示的单一位置。 不幸的是,一些六边形的包含多个位置,例如,一些原本里面他们夫妇岛屿。 我使用的复合物的图案来代表这些位置,但不能作为你的链接描述。 它是这样的(在Java中):
public interface Location {
Set<Army> getArmies();
}
public class SingleLocation implements Location {
public Set<Army> getArmies() {
return armies ;
}
private Set<Army> armies = new HashSet<Army>();
}
public class CompositeLocation implements Location {
public Set<Army> getArmies() {
Set<Army> armies = new HashSet<Army>();
for(Location subLocation: subLocations) {
armies.addAll(subLocation.getArmies());
}
return armies;
}
public void addSubLocation(Location location) {
subLocations.add(location);
}
private Set<Location> subLocations = new HashSet<Location>();
}
请注意,只有Composite
了合成方法,甚至不公开,它有孩子大部分客户的事实(在这个例子中,客户只希望从一个位置军队的名单-事实上,他们在许多子位置是无关紧要的)。
请记住,设计模式并不设置在石头的东西,你必须实现准确。 把它们当作食谱。 当您按照食谱做饭时,你当然可以只严格地遵守它。 然而,有些厨师会扔在配方自己的曲折。 其他人会连看都不看,因为他们是专家,可以在配方的精神一起扔东西,甚至没有考虑它。 这同样适用于设计模式。 他们是有延展性的食谱。
你也可以把这些SOLID原则太远。 如果你读罗伯特·马丁的文章,他指出,全线应用这些原则没有任何的思想会产生过于复杂的代码。 软件通过一系列的取舍和balancings的设计 - 有时你放弃纯实木,因为它产生较少的清洁复杂的代码。 如果你使你的代码完全封装,灵活,分离等,你会发明了一种新的编程语言:-)
我要说的是,在您的链接描述的复合模式违反了里氏替换原则 ,五大一个SOLID原则。
Component
有方法,只有有意义的Composite
如Add()
Leaf
从继承Component
,因此将有一个Add()
像任何其他方法Component
。 但是Leafs
没有孩子,所以下面的方法调用不能返回有意义的结果:
myLeaf.Add( someChild );
该呼叫将不得不抛出一个MethodNotSupportedException
,返回null
在一些其他的方式给调用者,添加一个孩子到一个或指示Leaf
没有意义。
因此,你不能这样对待一个Leaf
像任何其他Component
,因为如果你尝试,你会得到一个异常。 在里氏substition原则规定:
设q(x)为可证明的关于对象的属性类型T.则Q(Y)的X应为S型的对象ý真其中S是T的子类型
Components
有,您可以添加孩子给他们的财产。 但你不能孩子添加到Leaf
,尽管Leaf
是一个亚型Component
,它违反了原则。
GOF的书167页专门解决这个问题。
虽然综合类实现了
Add
和Remove
管理儿童行动,在综合模式的一个重要问题是哪些类声明这些行动......如果我们在组件声明这些操作,让他们有意义的叶类,或者我们应该声明只有在综合定义呢?该决定涉及安全与透明度之间的权衡:
- 在类层次结构的根定义子管理界面使您的透明度,因为你可以均匀地处理所有组件。 它的成本你的安全,但是,因为客户可以尝试做毫无意义的事情一样添加和叶删除对象。
- 在综合类定义孩子的管理,给您安全,因为任何试图从叶子中添加或删除对象将在编译时在C ++等静态类型语言被抓。 但你失去透明度,因为叶片和复合材料具有不同的接口。
我们已经在这种模式强调了安全的透明度。
最后一句是,模式违反类型安全原则的承认。 以固体计,这将主要是LSP,但可能ISP为好。 请注意,“ 声明其子类不使用方法 ”是ISP的过于简单化。 这些未使用的方法,真正的危险是,他们将要求其子类并不需要额外的依赖,模块之间的耦合增加。
使用复合材料让你来均匀地处理所有对象,并可能摆脱这是一个代码味道的一个明显标志“}这种”事。 所以它的第一眼尊重到LSP(里氏的)。 然而,正如你区分常见的方法实现,很可能开始违反LSP。 因此,国际海事组织,我们需要保持这种平衡。