复合图案是否坚固?
复合模式中的 Leaf 实现了 Component 接口,包括 Leaf 永远不会使用的 Add
、Remove
和 GetChild
方法。这似乎违反了接口隔离原则。
复合模式的用法也是如此 固体?
复合模式链接:http://www.dofactory.com/Patterns/PatternComposite.aspx
A Leaf in the Composite Pattern implements the Component interface, including Add
, Remove
, and GetChild
methods that a Leaf is never going to use. This seems to be a violation of the Interface Segregation Principle.
So is the usage of Composite Pattern SOLID?
link to Composite Pattern: http://www.dofactory.com/Patterns/PatternComposite.aspx
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您的链接和大多数书籍中描述的模式的真正气味是
Component
具有Composite
的方法。我认为这可能是因为这种模式相当古老,并且多年来一直以这种方式重复。我的看法是,只有Composite
才应该具有与合成相关的方法。我曾经将棋盘游戏转换为电脑游戏。游戏棋子被放置在地球地图上,分为六边形。 99% 的六边形代表一个位置。不幸的是,一些六边形包含多个位置,例如,有些六边形内部有几个岛屿。我使用复合模式来表示这些位置,但不像您的链接中所描述的那样。事情是这样的(在 Java 中):
请注意,只有
Composite
具有合成方法,甚至没有向大多数客户端公开它具有子级的事实(在本例中,客户端只想要来自某个地点的军队列表 - 它们位于许多子地点的事实是无关紧要的)。请记住,设计模式并不是您必须准确实施的一成不变的东西。将它们视为食谱。当你在烹饪时遵循食谱时,你当然可以完全按照它去做。然而,一些厨师会对食谱做出自己的改动。其他人甚至不会看它,因为他们是专家,可以不假思索地本着食谱的精神将一些东西组合在一起。设计模式也是如此。它们是可塑的食谱。
你也可能把这些坚实的原则看得太过分了。如果您阅读 Robert Martin 的文章,他会指出,不加思考地全面应用这些原则将会产生过于复杂的代码。软件是通过一系列的权衡和平衡来设计的——有时你会放弃纯粹的 SOLID,因为它会产生更清晰、更简单的代码。如果你能让你的代码完美封装、灵活、解耦等,你就发明了一种新的编程语言:-)
The real smell in the pattern as depicted in your link and most books is that
Component
has the methods of aComposite
. I think this is probably because the pattern is fairly old and has been repeated that way for years. My take is that only theComposite
should have any methods related to compositing.I once converted a board game over to a computer game. The playing pieces were placed on a map of earth, divided up into hexagons. 99% of all hexagons represented a single location. Unfortunately, a few of the hexagons contained multiple locations, for example, some had a couple islands inside them. I used the composite pattern to represent these locations, but not as depicted on your link. It was something like this (in Java):
Note that only the
Composite
has compositing methods, and doesn't even expose the fact that it has children to most clients (in this example, the client only wants a list of Armies from a location - the fact that they are at many sub-locations is irrelevant).Keep in mind design patterns are not set-in-stone things you must implement exactly. Think of them as recipes. When you follow a recipe while cooking, you certainly can just follow it exactly. However, some cooks will throw in their own twists on the recipe. Others won't even look at it because they are experts and can throw something together in the spirit of the recipe without even thinking about it. The same goes for design patterns. They are malleable recipes.
You can also take those SOLID principles too far. If you read Robert Martin's articles, he states that applying the principles across the board without any thought will yield overly complex code. Software is designed through a series of trade-offs and balancings - sometimes you forgo pure SOLID because it yields cleaner less complex code. If you were to make your code perfectly encapsulated, flexible, decoupled, etc., you will have invented a new programming language :-)
我想说,您的链接中描述的复合模式违反了 里氏替换原则< /a>,五个SOLID 原则之一。
Component
具有仅对Composite
有意义的方法,例如Add()
。Leaf
继承自Component
,因此它将像任何其他Component
一样具有Add()
方法。但是Leafs
没有子级,因此以下方法调用无法返回有意义的结果:myLeaf.Add( someChild );
该调用必须抛出
MethodNotSupportedException
,返回null
或以其他方式向调用者指示向Leaf
添加子项没有意义。因此,您不能像对待任何其他
Component
一样对待Leaf
,因为如果您尝试这样做,将会出现异常。里氏替换原理指出:Components
具有可以向其添加子级的属性。但是,您不能向Leaf
添加子级,即使Leaf
是Component
的子类型,这违反了原则。I would say that the Composite pattern as described in your link violates the Liskov substitution principle, one of the five SOLID principles.
Component
has methods that only make sense for aComposite
e.g.Add()
.Leaf
inherits fromComponent
so it will have anAdd()
method like any otherComponent
. ButLeafs
don't have children, so the following method call cannot return a meaningful result:myLeaf.Add( someChild );
That call would have to throw a
MethodNotSupportedException
, returnnull
or indicate in some other way to the caller that adding a child to aLeaf
does not make sense.Therefore you cannot treat a
Leaf
like any otherComponent
because you'll get an exception if you try to. The Liskov substition principle states:Components
have the property that you can add children to them. But you cannot add children to aLeaf
, even thoughLeaf
is a subtype ofComponent
, which violates the principle.GoF 书在第 167 页专门讨论了这个问题。
最后一句话承认该模式违反了类型安全原则。就 SOLID 而言,这主要是 LSP,但也可能是 ISP。请注意,“声明子类不使用的方法”是对 ISP 的过度简化。这些未使用的方法的真正危险在于它们将需要子类不需要的额外依赖项,从而增加了模块之间的耦合。
The GoF book specifically addresses this issue on page 167.
The last sentence is an admission that the pattern violates type-safety principles. In terms of SOLID, this would primarily be LSP, but possibly ISP as well. Note that "declaring methods which a subclass doesn't use" is an oversimplification of ISP. The real danger of those unused methods is that they will require additional dependencies which the subclass didn't need, increasing coupling between modules.
使用 Composite 可以让您统一处理所有对象,并可能摆脱“instanceOf”事物,这是代码味道的明显标志。所以乍一看这是对LSP(Liskov's)的尊重。然而,当您区分通用方法实现时,它可能会开始违反 LSP。因此,在我看来,我们需要保持这种平衡。
Using Composite lets you to treat all objects uniformly and possibly to get rid of "instanceOf" thing which is a clear sign of a code smell. So it's respecting to LSP (Liskov's) at a first glance. However, as you differentiate common method implementations, it's likely to start to violate LSP. So, IMO, we need to keep that balance.