Title: Composite design pattern
1Composite design pattern
2Motivating example Drawing
- A drawing consists of a number of basic drawing
elements - points, lines, circles, rectangles etc
3An example drawing
- A drawing may for instance consist of a circle
and four lines - The object diagram
- The visual appearance
4A more complicated drawing
- We may want to draw more complicated things
- The same group of a circle and four lines appears
many times - we define that group to be a new drawing element,
called StickMan - we can use StickMan as a new basic drawing element
5Motivating example Drawing
- A composite drawing element acts as a basic
drawing element - the difference is that it contains other drawing
element - the contained elements may be basic or composite
6An example of a composite drawing
- A drawing may consist of two stick men and a
circle - The object diagram
- The visual appearance
7The Composite Design Pattern
- Intent
- Compose objects into tree structures to represent
part-whole hierarchies. Composite lets clients
treat individual objects and compositions of
objects uniformly.
From Gamma et al Design Patterns, Elements of
Reusable Object-Oriented Software
8The Composite Design Pattern motivation
- Motivation
- Graphics applications like drawing editors and
schematic capture systems let users build complex
diagrams out of simple components. 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. - But there's a problem with this approach Code
that uses these classes must treat primitive and
container objects differently, even if most of
the time the user treats them identically. Having
to distinguish these objects makes the
application more complex. The Composite pattern
describes how to use recursive composition so
that clients don't have to make this distinction.
From Gamma et al Design Patterns, Elements of
Reusable Object-Oriented Software
9The Composite Design Pattern
From Gamma et al Design Patterns, Elements of
Reusable Object-Oriented Software
10The Composite Design Pattern
- The key to the Composite pattern is an abstract
class that represents both primitives and their
containers. For the graphics system, this class
is Graphic. Graphic declares operations like Draw
that are specific to graphical objects. It also
declares operations that all composite objects
share, such as operations for accessing and
managing its children. - The subclasses Line, Rectangle, and Text (see
preceding class diagram) define primitive
graphical objects. These classes implement Draw
to draw lines, rectangles, and text,
respectively. Since primitive graphics have no
child graphics, none of these subclasses
implements child-related operations. - 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.
From Gamma et al Design Patterns, Elements of
Reusable Object-Oriented Software
11Drawing in a graphical environment
- The environment will call a graphical element to
draw itself - the graphical element is typically a window or a
frame - A graphics context is received as parameter to
the draw function - a graphics context is typically an area on the
screen - the window will ask its contents to draw itself
on the graphics context - a text or a drawing
- the leaves of the drawing ask the graphics
context for the correct kind of drawing operation - drawLine, drawCircle etc
- with the relevant parameters from the graphical
element - the composite parts will ask all its components
to draw themselves recursively
12Class diagram for drawing in a graphical
environment
13Drawing in various environments
- Drawing can be done uniformly on different
surfaces - By defining subclasses of the class
GraphicsContext - Drawing on a text surface is easy
- Implement for instance drawLine(Point p1, Point
p2) as - cout ltlt (( ltlt p1.x ltlt , ltlt p1.y ltlt ),(
- ltlt p2.x ltlt , ltlt p2.y ltlt )) ltlt endl
- The figure elements will not notice any
difference
14Combination with handle-body
- In combination with the handle-body pattern a
part of a figure can change shape without losing
its position in the figure
15Handle-body object diagram stickman circle
16Implementation in C Equipment
From Gamma et al Design Patterns, Elements of
Reusable Object-Oriented Software
17Example code iterating
- A default implementation of netPrice might use
createIterator - factory method to return a default iterator on
the equipment collection - to sum the net prices of the subequipment
- Currency CompositeEquipmentnetPrice ()
- Iterator i createIterator()
- Currency total 0
- for (i-gtfirst() !i-gtisDone() i-gtnext())
- total i-gtcurrent()-gtnetPrice()
-
- delete i
- return total
-
- Do not forget to delete the iterator after use
From Gamma et al Design Patterns, Elements of
Reusable Object-Oriented Software
18Example code a composite class
- Now we can represent a computer chassis as a
subclass of CompositeEquipment called Chassis.
Chassis inherits the child-related operations
from CompositeEquipment. - class Chassis public CompositeEquipment
- public
- Chassis(const char)
- virtual Chassis()
-
- virtual Watt Power()
- virtual Currency NetPrice()
- virtual Currency DiscountPrice()
-
From Gamma et al Design Patterns, Elements of
Reusable Object-Oriented Software
19Example code a simple application
- We can define other equipment containers such as
Cabinet and Bus in a similar way. That gives us
everything we need to assemble equipment into a
(pretty simple) personal computer - Cabinet cabinet new Cabinet("PC Cabinet")
- Chassis chassis new Chassis("PC Chassis")
- cabinet-gtAdd(chassis)
- Bus bus new Bus("MCA Bus")
- bus-gtAdd(new Card("16Mbs Token Ring"))
- chassis-gtAdd(bus)
- chassis-gtAdd(new FloppyDisk("3.5in Floppy"))
- cout ltlt "The net price is " ltlt
chassis-gtNetPrice() - ltlt endl
From Gamma et al Design Patterns, Elements of
Reusable Object-Oriented Software
20Another composite example
- A graphical windowing system
21The composite dilemma
- A composite super-class has two categories of
subclasses - Container classes with member objects contained
in them - End leave classes without member objects
- How can component be uniformly accessed?
- Leave classes do not have operations for
accessing member objects - Gamma et al. present two basic approaches
- make the operations dummy in the component base
class - allows a uniform access to all component objects,
whether or not containers - not implement the container operations
- does not allow a uniform access to components
since it requires the client to type check the
component before call
22Data abstraction and hierarchy
- Barbara Liskov formulated a principle for
subtyping. - This principle tells when inheritance can be used
for subtyping.
23Data abstraction and hierarchy
- It is commonly called Liskovs substitution
principle - It can be translated to contracts
- the contracts for T express the behavior of o2
- the contracts for T must be valid even for S if
o1 shall be substituted for o2
24Liskovs substitution principle quoted
- 3.3. Type hierarchy
- A type hierarchy is composed of subtypes and
supertypes. The intuitive idea of a subtype is
one whose objects provide all the behavior of
objects of another type (the supertype) plus
something extra. - What is wanted here is something like the
following substitution property - If for each object o1 of type S there is an
object o2 of type T such that for all programs P
defined in terms of T, the behavior of P is
unchanged when o1 is substituted for o2, then S
is a subtype of T.
From Barbara Liskov, Data Abstraction and
Hierarchy, OOPSLA '87 Addendum to the
Proceedings, October 1987, pages 17-34.
25Solution to the composite dilemma using contracts
- Liskovs substitution principle dictates that
both container and leave classes should be
handled in a uniform way - they should obey the same contract
- Introduce an operation in the component class to
inquire if it is a container or not - this kind of operation is called a predicate
- boolean isContainer()
- Use this function in the precondition of the
container specific functions - pre isContainer()
- post the element is added to the container
- void add(Component aComponent)
26Consequences of the proposed solution
- The responsibility is defined to be with the
client - It may be implemented as a do nothing function
in the Component base class - The Container subclass should override it to
perform container operations - The container operations should only be called on
container objects - Everything else is an error by client and the
server (the component) is not bound by the
postcondition of the contract - It is correct to do nothing in such a case
- Every other imaginable implementation is also
correct