复合图案是否坚固?

发布于 2024-08-08 05:08:29 字数 478 浏览 5 评论 0原文

复合模式中的 Leaf 实现了 Component 接口,包括 Leaf 永远不会使用的 AddRemoveGetChild 方法。这似乎违反了接口隔离原则。

复合模式的用法也是如此 固体

复合模式链接: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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

等往事风中吹 2024-08-15 05:08:29

您的链接和大多数书籍中描述的模式的真正气味是 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 具有合成方法,甚至没有向大多数客户端公开它具有子级的事实(在本例中,客户端只想要来自某个地点的军队列表 - 它们位于许多子地点的事实是无关紧要的)。

请记住,设计模式并不是您必须准确实施的一成不变的东西。将它们视为食谱。当你在烹饪时遵循食谱时,你当然可以完全按照它去做。然而,一些厨师会对食谱做出自己的改动。其他人甚至不会看它,因为他们是专家,可以不假思索地本着食谱的精神将一些东西组合在一起。设计模式也是如此。它们是可塑的食谱。

你也可能把这些坚实的原则看得太过分了。如果您阅读 Robert Martin 的文章,他会指出,不加思考地全面应用这些原则将会产生过于复杂的代码。软件是通过一系列的权衡和平衡来设计的——有时你会放弃纯粹的 SOLID,因为它会产生更清晰、更简单的代码。如果你能让你的代码完美封装、灵活、解耦等,你就发明了一种新的编程语言:-)

The real smell in the pattern as depicted in your link and most books is that Component has the methods of a Composite. I think this is probably because the pattern is fairly old and has been repeated that way for years. My take is that only the Composite 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):

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>();
}

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 :-)

陌生 2024-08-15 05:08:29

我想说,您的链接中描述的复合模式违反了 里氏替换原则< /a>,五个SOLID 原则之一。

Component 具有仅对 Composite 有意义的方法,例如 Add()Leaf 继承自 Component,因此它将像任何其他 Component 一样具有 Add() 方法。但是 Leafs 没有子级,因此以下方法调用无法返回有意义的结果:

myLeaf.Add( someChild );

该调用必须抛出 MethodNotSupportedException,返回 null 或以其他方式向调用者指示向 Leaf 添加子项没有意义。

因此,您不能像对待任何其他 Component 一样对待 Leaf,因为如果您尝试这样做,将会出现异常。里氏替换原理指出:

令 q(x) 为可证明的性质
类型 T 的对象 x 。那么 q(y) 应该
对于 S 类型的对象 y 为真,其中
S 是 T 的亚型。

Components 具有可以向其添加子级的属性。但是,您不能向 Leaf 添加子级,即使 LeafComponent 的子类型,这违反了原则。

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 a Composite e.g. Add(). Leaf inherits from Component so it will have an Add() method like any other Component. But Leafs 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, return null or indicate in some other way to the caller that adding a child to a Leaf does not make sense.

Therefore you cannot treat a Leaf like any other Component because you'll get an exception if you try to. The Liskov substition principle states:

Let q(x) be a property provable about
objects x of type T. Then q(y) should
be true for objects y of type S where
S is a subtype of T.

Components have the property that you can add children to them. But you cannot add children to a Leaf, even though Leaf is a subtype of Component, which violates the principle.

空心↖ 2024-08-15 05:08:29

GoF 书在第 167 页专门讨论了这个问题。

虽然 Composite 类实现用于管理子项的添加删除操作,但 Composite 模式中的一个重要问题是哪些类 >声明这些操作...我们应该在组件中声明这些操作并使它们对叶类有意义,还是应该仅在组合中声明和定义它们?

该决定涉及安全性和透明度之间的权衡:

  • 在类层次结构的根部定义子管理接口可以为您提供透明度,因为您可以统一对待所有组件。然而,这会损害您的安全,因为客户端可能会尝试做一些无意义的事情,例如在叶子中添加和删除对象。
  • 在 Composite 类中定义子管理可以保证安全,因为任何在叶子中添加或删除对象的尝试都将在静态类型语言(如 C++)的编译时被捕获。但你会失去透明度,因为叶子和复合材料有不同的界面。

在这种模式中,我们强调透明度而非安全性。

最后一句话承认该模式违反了类型安全原则。就 SOLID 而言,这主要是 LSP,但也可能是 ISP。请注意,“声明子类不使用的方法”是对 ISP 的过度简化。这些未使用的方法的真正危险在于它们将需要子类不需要的额外依赖项,从而增加了模块之间的耦合。

The GoF book specifically addresses this issue on page 167.

Although the Composite class implements the Add and Remove operations for managing children, an important issue in the Composite pattern is which classes declare these operations... Should we declare these operations in the Component and make them meaningful for Leaf classes, or should we declare and define them only in Composite?

The decision involves a trade-off between safety and transparency:

  • Defining the child management interface at the root of the class hierarchy gives you transparency, because you can treat all components uniformly. It costs you safety, however, because clients may try to do meaningless things like add and remove objects from leaves.
  • Defining child management in the Composite class gives you safety, because any attempt to add or remove objects from leaves will be caught at compile-time in a statically typed language like C++. But you lose transparency, because leaves and composites have different interfaces.

We have emphasized transparency over safety in this pattern.

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.

春风十里 2024-08-15 05:08:29

使用 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文