Composite

From CSSEMediaWiki
(Difference between revisions)
Jump to: navigation, search
Line 55: Line 55:
 
==Conflicts==
 
==Conflicts==
 
* [[Avoid no-op overrides]]: The Composite pattern tells us to put the addChild(), removeChild() and getChildren() methods in the Component class. However, this means that we need to override them using a no-op in the leaf component which violates Riel's heuristic [[Avoid no-op overrides]] and smells a little of [[Refused bequest smell]].
 
* [[Avoid no-op overrides]]: The Composite pattern tells us to put the addChild(), removeChild() and getChildren() methods in the Component class. However, this means that we need to override them using a no-op in the leaf component which violates Riel's heuristic [[Avoid no-op overrides]] and smells a little of [[Refused bequest smell]].
 +
 +
==Options==
 +
 +
There are several ways that Composite can be implemented, each of which seems to break some heuristics.
 +
 +
If we follow the Gang of Four design, we get no-op overrides in the concrete leaf classes. This conflicts with [[Avoid no-op overrides]]. Another problem with this option is that the contract for the addChild() and removeChild() methods is very loose because we can't guarantee that children were actually removed or added if we happen to call that method on a leaf rather than a composite.
 +
 +
If we move the addChild(), removeChild() and getChildren() into the composite class to avoid the no-op overrides, clients now have to be aware of the difference between leaves and composites. This means that if they want to add a child to a composite, they have to make sure they have a composite and potentially need to use downcasts to get one. This violates the [[Avoid downcasting]] heuristic. On the other hand, at least clients can now be sure that the child will actually be added to the composite unlike in the previous option.
  
 
== See also ==
 
== See also ==

Revision as of 01:23, 10 August 2009

Composite pattern

The Composite pattern is useful whenever one has a class which may contain instances of itself. Common computer science examples are nodes in a tree structure, or shapes in a graphical model. More concrete physical world examples include regions (which can potentially contain many smaller regions), schools of fish (where those fish may swim, eat, etc in a collective fashion), and divisions/teams in a workplace.

The key feature of this pattern is a subclass which both extends the superclass, and contains (though an aggregation or composition relationship) a number of instances of the superclass.

The advantage gained from this structure is that we can transparently use a Composite Instance just as with a Primitive instance. shape.draw(), for example could draw any shape, from a primitive to a highly complex diagram or model.


Contents

Features

Classes

  • Interface (Abstract class) that represents Component.
  • Concrete Composite class that implements Component.
  • Concrete leaf class that implements Component.
  • Client class.

Attribute

  • Composite maintains a collection of Components. E.g, Collection<Component>

Methods

  • Component has abstract methods to add and remove an Component to and from.
  • Composite has concrete method to add an instance of Component that shared by Composites and leaves on the collection.
  • Composite has concrete method to remove an instance of Component that shared by Composites and leaves from the collection.

Relationships

  • Composite contains both Composite and leaf as Component.
  • All Composites and leaves are seen as Component by Client.

Use When

  • You want to represent a hierarchy of parts and whole objects.
  • You want clients to not know about whether they are dealing with a composition of objects or a single object.

Recognising the pattern

Classes: Composite, Component, multiple Leaves

  • Inheritance hierarchy that has a Component interface
  • Component interface has declarations for add() and remove() methods for adding/removing Components.
  • Composite class has private collection of Components.

Consequences

  • Primitive objects can be composed into more complex objects, which in turn can be composed into even more complex objects. Clients treat primitive objects and composite objects in the same way.
  • Simplifies the client because it can treat simple objects and composites the same way.
  • Makes it easy to add new components or composites because the client code will not be affected.
  • Can make the design too general because it is hard to restrict what components can be added to composites.

Related Patterns

  • Chain of Responsibility: This pattern often uses a component-parent link.
  • Decorator: This pattern is often used together with Composite.
  • Flyweight: This pattern lets you share components.
  • Iterator: This pattern can be used to traverse composites.
  • Visitor: This pattern can be used to pull out behavior that would usually be in the Composite and Leaf classes.

Conflicts

  • Avoid no-op overrides: The Composite pattern tells us to put the addChild(), removeChild() and getChildren() methods in the Component class. However, this means that we need to override them using a no-op in the leaf component which violates Riel's heuristic Avoid no-op overrides and smells a little of Refused bequest smell.

Options

There are several ways that Composite can be implemented, each of which seems to break some heuristics.

If we follow the Gang of Four design, we get no-op overrides in the concrete leaf classes. This conflicts with Avoid no-op overrides. Another problem with this option is that the contract for the addChild() and removeChild() methods is very loose because we can't guarantee that children were actually removed or added if we happen to call that method on a leaf rather than a composite.

If we move the addChild(), removeChild() and getChildren() into the composite class to avoid the no-op overrides, clients now have to be aware of the difference between leaves and composites. This means that if they want to add a child to a composite, they have to make sure they have a composite and potentially need to use downcasts to get one. This violates the Avoid downcasting heuristic. On the other hand, at least clients can now be sure that the child will actually be added to the composite unlike in the previous option.

See also


Personal tools