Composite pattern
From Free net encyclopedia
Template:Cleanup-date Template:Expert In Computer Science, the composite pattern is a design pattern: "A general solution to a common problem in software design."
Motivation: In object-oriented programming, a Composite is an object (e.g. a shape) designed as a composition of one-or-more similar objects (other kinds of shapes/geometries), all exhibiting similar functionality. This is known as a "has-a" relationship between objects. The key concept is that you can manipulate a single instance of the object just as you would a group of them. The operations you can perform on all the composite objects often have a least common denominator relationship. For example, when resizing a single shape to fill the screen, surely you would expect/desire that resizing a group of shapes would have the same effect.
When to use: You find that you are using multiple objects in the same way, and often have nearly identical code to handle each of them -- the only differences being that you are manipulating an instance of a 'circle' versus a 'square', for instance. Useful if differentiation doesn't need to exist, and it would be easier to think of them as homogeneous. (Of course, you could still provide functionality to manipulate only a single instance -- like selecting an item from a list instead of operating on the whole list.)
Compose means a special thing: it refers to building objects using DelegationConcept. Delegation-composition hangs onto constituent parts-using references. By contrast, mixins inherit from each part. MixIns prevent returning a WholeObject in response to requests for information, and they prevent having more than one of any given part.
Contents |
Structure
- Component
- declares the interface for object composition
- implements default behaviour
- declares an interface for accessing and managing the child components
- Leaf
- represents leaf objects in the composition
- Composite
- defines behaviour for components having children
- stores child components
- implements child-related operations io the Component interface
- Client
- manipulates objects in the composition through the Composite interface
Examples
Java
import java.util.*; interface Component { public String defaultMethod(); public ArrayList<Component> getChildren(); public boolean addComponent(Component c); public boolean removeComponent(Component c); } class Composite implements Component { private String id; private ArrayList<Component> components = new ArrayList<Component>(); public Composite(String identification) { id = identification; } public String defaultMethod() { String s = " (" + id + ":"; for (Component child : getChildren()) s += " " + child.defaultMethod(); return s + ") "; } public ArrayList<Component> getChildren() { return components; } public boolean addComponent(Component c) { return components.add(c); } public boolean removeComponent(Component c) { return components.remove(c); } } class Leaf implements Component { private String id; public Leaf(String identification) { id = identification; } public String defaultMethod() { return id; } public ArrayList<Component> getChildren() { return null; } public boolean addComponent(Component c) { return false; } public boolean removeComponent(Component c) { return false; } } class CompositePattern { public static void main(String[] args) { Composite england = new Composite("England"); Leaf york = new Leaf("York"); Leaf london = new Leaf("London"); england.addComponent(york); england.addComponent(london); england.removeComponent(york); Composite france = new Composite("France"); france.addComponent(new Leaf("Paris")); Composite europe = new Composite("Europe"); europe.addComponent(england); europe.addComponent(france); System.out.println( europe.defaultMethod() ); } }
The output is: "(Europe: (England: London) (France: Paris) )"
Perl (needs revising)
Objects may be members of a number of linked lists in our system. The linked lists organize the objects by different criteria.
package LinkedList; use ImplicitThis; ImplicitThis::imply(); sub new { my $type = shift; bless { next=>'', previous=>'' }, $type; } sub next { return $next; } sub set_next { $next = shift; return 1; } sub previous { return $previous; } sub set_previous { $previous = shift; return 1; } sub append { my $ob = shift; $ob->isa(__PACKAGE__) or die; $next or do { $next = $ob; $ob->set_previous($this); return 1; } $ob->set_next($next); $next->set_previous($ob); $ob->set_previous($this); $this->set_next($ob); return 1; }
This can be inherited, but inheriting it multiple times doesn't do any good: one only ever has one instance of the LinkedList this way - oneself. Using composition gives the desired result:
package TriceQueuedObject; use LinkedList; use ImplicitThis; ImplicitThis::imply(); sub new { my $type = shift; my $me = { sort_order => new LinkedList, size_order => new LinkedList, save_order => new LinkedList, @_ }; bless $me, $type; } # create accessors that defer the action to each object, for each object composing us: # method A: see text below sub next_sort { return $sort_order->next(); } sub previous_sort { return $sort_order->previous(); } sub set_next_sort { return $sort_order->set_next(@_); } sub append_sort { return $sort_order->append(@_); } sub next_size { return $size_order->next(); } sub previous_size { return $size_order->previous(); } sub set_next_size { return $size_order->set_next(@_); } sub append_size { return $size_order->append(@_); } sub next_save { return $save_order->next(); } sub previous_save { return $save_order->previous(); } sub set_next_save { return $save_order->set_next(@_); } sub append_save { return $save_order->append(@_); } # directly return references to objects that compose us: # method B: see text below sub get_sort_order { return $sort_order; } sub get_size_order { return $size_order; } sub get_save_order { return $save_order; }
"Method A" and "method B" illustrate two very different approaches to giving users of the object access to the parts. "Method A" creates all new accessors which do their work by calling accessors in the composing objects. "Method B" simply returns the composing objects and lets the user call the methods directly. For example:
# using method A: $ob->next_sort($ob2); # using method B: $ob->get_sort_order()->set_next($ob2);
Which method is preferable varies. If the object is merely a container for other objects, B makes more sense. If the object is a Facade, providing a new interface to several objects, A makes more sense. If the contained objects are considered to be implementation dependent, and having to support returning intermediate objects in the future is not desirable, A allows better hiding of the implementation. B makes for shorter code and less typing when the relationship between the objects is not likely to change.
Each LinkedList instance is a "delegate" in this example. The methods that propagate requests to them are "delegate methods".
See also
- Design Patterns: The book that started it all.
- Mixin
- Facade pattern
- Decorator pattern
- Law of Demeter
- Delegation pattern
- Builder pattern
- Abstract factory pattern
External links
- Description from the Portland Pattern Repository
- Class::Delegation on CPAN
- Chinese Ring Puzzle Applet
- "Monkey Delegation" by Michael G. Schwern
- Lazy Load
- "The End of Inheritance: Automatic Run-time Interface Building for Aggregated Objects" by Paul Baranowski
Parts of this article originated from the Perl Design Patterns Bookde:Kompositum (Entwurfsmuster) es:Composite (patrón de diseño) fr:Objet composite it:Composite pattern