Anda di halaman 1dari 8

openloop.

com

http://www.openloop.com/softwareEngineering/patterns/designPattern/dPattern_wholePart.htm

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

Anda mungkin juga menyukai