ghytred.com


NewsLook for Outlook

Patterns and OO
CompositePattern
The Web Site you seek
Cannot be located, so
Waste your time here

Contents

Composite

Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

Places where Composites might be used:

  • Product Assemblies: We have products A, B, C, D, E, etc. We also have product X made up of 2 A�s, a C and a D. We also have Product Y made up of an H, a J and two X�s.
  • GUIs nearly always have objects which in turn contain other objects (e.g. a Form containing a Panel containing a TextBox and a GroupBox containing radio buttons, etc).
  • A compound Document which contains other objects, each of which can be a compound document.

Composites can be traversed recursively, e.g.:

        private void Traverse(XmlNode aNode) {
            // do something with the node

            // and then the children
            foreach(XmlNode childNode in aNode.ChildNodes) {
                Traverse(childNode);
            }
        }

This is almost their defining feature in my opinion.

The canonical pattern has an abstract Component class with an Add and a Remove method, each taking a Component object as a parameter; it also has a method for getting a child or a list of all children. It also has one or more methods which do something specific to the individual objects (e.g. for the GUI example a BackColor property).

Composite inherits from Component, and stores child Components. It defines the behaviour of components which can have children (e.g. Form, Panel)

Leaf inherits from Component but has no children. It defines the behaviour of Components that cannot have children (e.g. TextBox, Label).

Do we really need separate Leaf and Composite classes? Should Composite inherit from Leaf, and do away with Component?

Consequences:

Client code has to expect the Component, not the Leaf; but this is still simple code. GoF warn that this pattern can become overly general.

From http://home.earthlink.net/~huston2/dp/composite.html, various comments. Note particularly the author of the last one � one of the GoF.

  • Being able to treat a heterogeneous collection of objects atomically (or transparently) requires that the "child management" interface be defined at the root of the Composite class hierarchy (the abstract Component class). However, this choice costs you safety, because clients may try to do meaningless things like add and remove objects from leaf objects. On the other hand, if you "design for safety", the child management interface is declared in the Composite class, and you lose transparency because leaves and Composites now have different interfaces. [Bill Burcham]
  • Smalltalk implementations of the Composite pattern usually do not have the interface for managing the components in the Component interface, but in the Composite interface. C++ implementations tend to put it in the Component interface. This is an extremely interesting fact, and one that I often ponder. I can offer theories to explain it, but nobody knows for sure why it is true. [Ralph Johnson]
  • My Component classes do not know that Composites exist. They provide no help for navigating Composites, nor any help for altering the contents of a Composite. This is because I would like the base class (and all its derivatives) to be reusable in contexts that do not require Composites. When given a base class pointer, if I absolutely need to know whether or not it is a Composite, I will use dynamic_cast to figure this out. In those cases where dynamic_cast is too expensive, I will use a Visitor. [Robert Martin]
  • Composite doesn't force you to treat all Components as Composites. It merely tells you to put all operations that you want to treat "uniformly" in the Component class. If add, remove, and similar operations cannot, or must not, be treated uniformly, then do not put them in the Component base class. Remember, by the way, that each pattern's structure diagram doesn't define the pattern; it merely depicts what in our experience is a common realization thereof. Just because Composite's structure diagram shows child management operations in the Component base class doesn't mean all implementations of the pattern must do the same. [John Vlissides]


[This document was original the backup for a presentation which split the attendees into small groups (2 or 3) and asked them to implement in more or less detail the following specifications.
There is an implementation of Composite below the two exercises.]

Spec I

A Retailer has an existing Stock Control and Order Processing system (written in C#, of course). This has an existing Product class which contains properties for Description, Price, Cost, and a ProductType (a class containing Description and Code).

He wishes to introduce some new products by packaging existing ones into �kits� � for example including two pairs of ear-muffs with a toy drum. The cost of a kit will be �1 more than the sum of the cost of the individual products. The Sale Price will be the sum of the individual Prices, after a percentage has been added on to the individual product�s Price (where the percentage will depend on the ProductType of the individual product); however if this calculated price is less than 115% of the Kit�s cost, the Price will be 115% of the Kit�s cost.

Spec II

Assuming that you were writing a system from scratch for the above problem, how would you implement it differently?


Sample implementation of Composite:

using System;
using System.Collections;

namespace GofPatterns.Composite {
	public class Composite {
		private ArrayList children;
		protected object whatThisNodeIs;

		public Composite(object whatThisNodeIs) {
			children = new ArrayList();
			this.whatThisNodeIs = whatThisNodeIs;
		}
		public object BaseObject {
			get { return whatThisNodeIs; }
		}
		public override string ToString() {
			return string.Format(
				"Composite containing '{0}' and {1} children", 
				whatThisNodeIs.ToString(), this.Count
				);
		}

		public int Count {
			get {
				int iCount = 0;
				foreach (Composite c in children) {
					iCount += c.Count + 1;
				}
				return iCount;
			}
		}


		public void Add(Composite toAdd) {
			children.Add(toAdd);
		}
		public void Remove(Composite toRemove) {
			if (children.Contains(toRemove)) {
				children.Remove(toRemove);
			}
		}
		public void RemoveAt(int indexToRemove) {
			if (indexToRemove < children.Count) {
				children.RemoveAt(indexToRemove);
			}
		}
		public ArrayList Children {
			get { return children; }
		}
	}

}

The client would look like this. The following code creates and fills a composite and then shows it in a WinFowms TreeView.

	private GofPatterns.Composite.Composite composite;
	private void MakeAndShowComposite() {
		composite = new Composite("Top level composite");
		for (int i = 0; i < 3; i++) {
			Composite child = 
				new Composite("Child " + i.ToString());
			composite.Add(child);
			for (int j = 0; j < 5; j++) {
				Composite child2 = 
					new Composite("Child " + 
						j.ToString() + " of " + i.ToString());
				child.Add(child2);
			}
		}
		ShowTV();
	}
	/// 
	/// A TreeView is in fact a composite of TreeNodes,
	/// so this is a good way of showing one :)
	/// 
	private void ShowTV() {
		tv1.BeginUpdate();
		tv1.Nodes.Clear();
		TreeNode topNode = new TreeNode(composite.ToString());
		topNode.Tag = composite;
		tv1.Nodes.Add(topNode);
	        ShowNodeChildren(topNode);
		tv1.ExpandAll();
		tv1.EndUpdate();
	}
	private void ShowNodeChildren(TreeNode parent) {
		GofPatterns.Composite.Composite comp = 
				parent.Tag as GofPatterns.Composite.Composite;
		foreach (GofPatterns.Composite.Composite c in comp.Children) {
			TreeNode node = new TreeNode(c.ToString());
			node.Tag = c;
			parent.Nodes.Add(node);
			ShowNodeChildren(node);
		}
	}

Return to top