Design Pattern: Whole-Part

 

Introduction

 

Example

 

Context

 

Problem

 

Solution

 

Structure

Class: Whole

Responsibility:

  • Aggregates several smaller objects
  • Provides services built on top of part objects
  • Acts as a wrapper around its constituent parts

Collaborators: Part

 

Class: Part

Responsibility:

  • Pepresents a particular object and its services

Collaborators: -

Component

Declares the interface for objects in the Whole.

Implements default behavior for the interface common to all classes, as appropriate.

Declares an interface for accessing and managing its child components

Defines an interface for accessing a component’s parent in the recursive structure, and implements it if that is appropriate.

Leaf

Represents leaf objects in the Whole. A leaf has no children.

Defines behavior for primitive objects in the Whole.

Composite

Defines behavior for components having children.

Stores child components.

Implements child-related operations in the Component interface.

Client

Manipulates objects in the Whole through the Whole interface.

 

Implementation

Using Graphic as example

 

In General

  1. Design the public Interface of the Whole
  2. Separate the Whole into Parts, or make it from existing ones (Use either bottom-up, top-down or both mixed)
  3. Use existing Parts from component libraries or class libraries or package, specify their collaboration if you use bottom-up approach
  4. Partition the Whole's services into smaller collaborating services and map these collaborating services to separate Parts if you follow a top-down approach
  5. Specific the services of the Whole in terms of services of the Parts
  6. Implement the Parts (Recursively if Parts are not leaf)
  7. Implement the Whole by putting all the Parts together

 

Cleaning Up

Explicit parent references

Simplify the traversal and management of a composite structure

Simplify moving up the structure and deleting a component

Maximizing the Component interface

The Component class should define as many common operations for Composite and Leaf classes as possible

Do we conflict the principle of class hierarchy design

Principle: a class should only define operations that are meaningful to its subclass.

There are many operations that Component supports that do not seem to make sense for Leaf classes

Trade off between principle and pattern

Using abstract class ... but do not overuse it

Use interface for "multiple parents"

Declaring the Part management operations

Should we declare these operations in the Whole and make them meaningful for Part/Leaf classes?

Should we declare and define them only in Composite and its subclasses?

What is the trade-off between safety and transparency?

 

Sample Code

class Equipment 
{
	public Equipment(String);

	public String name() { return name; }
	public abstract Watt Power();
	public abstract Currency NetPrice();
	public abstract Currency DiscountPrice();
	public abstract void Add(Equipment*);
	public abstract void Remove(Equipment*);
	public abstract Equipment* CreateIterator();
	private String name;
};

 

class FlppyDisk extends Equipment 
{
	public FloppyDisk(String);
	public abstract Watt Power();
	public abstract Currency NetPrice();
	public abstract Currency DiscountPrice();
};

 

class CompositeEquipment extends Equipment 
{
	
	CompositeEquipment(String);

	public abstract Watt Power();
	public abstract Currency NetPrice();
	public abstract Currency DiscountPrice();
	public abstract void Add(Equipment*);
	public abstract void Remove(Equipment*);
	public abstract Equipment* CreateIterator();

};
class Chassis : public CompositeEquipment 
{
	public:
		Chassis(const char*);
		virtual ~Chassis();
		virtual Watt Power();
		virtual Currency NetPrice();
		virtual Currency DiscountPrice();
};

 

Applicability

 

Consequences

Advantages

Changeability of Parts

Whole encapsulates the Parts and thus conceals them from its client

Separation of concerns

Each concern is implemented by a separate Part

Reusability

Parts of Whole can resued in other aggregate objects

Defines class hierarchies consisting of primitive objects and composite objects.

Primitive objects can be composed into more complex objects, which in turn can be composed, and so on recursively.

Makes the client simple.

Clients can treat composite structures and individual objects uniformly.

Makes it easier to add new kinds of components.

Newly defined Composite or Leaf subclasses work automatically with existing structures and client code.

Disadvantages

Makes your design overly general

The disadvantage of making it easy to add new components is that it makes it harder to restrict the components of a composite. For example, if operations are provided in Parts ... how can we prevent the make of some non-sense object

Lower efficiency through indirection

Wrappers are wrapper. You call somthing, something calls another thing and so on ... slow in some case

Complexity of decomposition into Parts might be an art

Depends on bottom-up, top-down, domain experts, technology used, etc