You are on page 1of 9

Design Pattern: Whole-Part

Introduction
 Sometimes called Composite
 Helps with the aggregation of components (parts) that together form a semantic unit
(whole).
 Direct access to the Parts is not possible
 Compose objects into tree structures to represent part-whole hierarchies.

 Whole-Part lets clients treat individual objects and compositions of object uniformly

 Supported in OO Programming Language

 Whole-Part is part of why OOP design the way it is

 Remember to read the text book

Example
 Graphics applications, like drawing editors, let users build complex diagrams out of
simple components. For example, a graph might contain lines, rectangles, texts, pictures

 The user can group components to form larger components, which in turn can be grouped
to form still larger components

 A simple implementation could define classes for graphical primitives such as Text and
Lines plus other classes that act as containers for these primitives

 Computer-Aided Design (CAD)

 Java Beans

 DevPack

 GL, OpenGL ?

 
Context
 Implementing aggregate objects

Problem
 A complex object should either be decomposed into smaller objects or composed of
existing objects, to support reusability, changeability and the recombination of the
constituent objects in other types of aggregate

 Clients should see the aggregate object as an atomic object that does not allow any direct
access to its constituent parts

 Code that uses these classes must treat primitive (Text, Lines, etc.) and container
(Graphic) objects differently

 Having to distinguish these objects makes the application more complex.

Solution
 Use a component that encapsulates smaller objects, and prevents clients from accessing
these constituent parts directly

 Use an interface as the only means of access to the functionality of the encapsulated
objects (appear as a semantic unit)

 An assembly-parts relationship differentiates between a product and its parts or


subassemblies

 A container-contents relationship

 A collection-members relationship helps to group similar objects - such as an


organization and its members

Structure
 Two types of participant
 Two CRC cards

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

 Whole object: An aggregation of smaller objects

 The smaller objects are called Parts

 Whole object forms a semantic grouping of its Parts in that it coordinates and organizes
their collaboration

 The Whole uses the functionality of Parts objects for implementing services

 Some of the Whole methods are just placeholders for specific Part services. When that
kind of method is invoked, the Whole calls the relevant Parts service, and returns the
result to the client

 Use different relationships and further "divide" Whole-Part, the following "components"
can be inferred

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

 An abstract class that represents both primitives and their containers. Here, this class is
Graphic

 It declares operations, like Draw() here, that are specific to the object family

 It also declares operations that all composite object share, such operations for accessing
and managing its children, like Add(Graphic) here

 The subclasses define primitive objects, like Line, Rectangle, and Text here.

 Each primitive object implements operations, for example, class Line implements Draw()
to draw lines.

 Primitive objects have no child objects, none of these subclasses implements child-related
operations, for example, class Line does not implement child-related operation
Add(Graphic)

 The Picture class defines an aggregate of Graphic objects


 Picture implements Draw() to call Draw() on its children, and it implements child-related
operations accordingly

 Because the Picture interface conforms to the Graphic interface, Picture objects can
compose other Pictures recursively.

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?

 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. But you lost transparency, because leaves and composites have different
interface.

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

 Equipment such as computer and stereo components are often organized into part-whole
or containment hierarchies

 Equipment class defines an interface fro all equipment in the part-whole hierarchy.

class FlppyDisk extends Equipment


{
public FloppyDisk(String);
public abstract Watt Power();
public abstract Currency NetPrice();
public abstract Currency DiscountPrice();
};

 Subclasses of Equipment might include Leaf classes that represent disk drives, integrated
circuits, and switches.

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

};

 CompositeEquipment is the base class for equipment that contains other equipment

 It is a composite class

 It is a subclass of Equipment

class Chassis : public CompositeEquipment


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

Applicability
 Use the Whole-Part pattern when:

 You want to represent part-whole hierarchies of objects


 You want clients to be able to ignore the difference between compositions of objects and
individual objects. Clients will treat all objects in the composite structure uniformly.

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

You might also like