Windows XP scans the user's menus to see which menu branch has
a recently installed program that shows the operation of the pattern. The
individual programs which can be run are leaves while the branches of the
sub-menus represent composites.
Scenario - Sample Code
The sample code shows the integration of the composite
pattern with a windows form control which is a natural fit for showing
composite leaf/branch structures: The treeview control. Of course, the aspects
related to the treeview fall into the category of extra-pattern issues, but at
least you get to see real-life integration with language features.
So what does it do? It allows you to model in a tree
structure all the available rooms which can be booked in a resort area. Since Jamaica has four major resort areas, it allows you to add the four of them. These resort
areas behave like composite items. It also allows you to add other composites,
such as Hotels and hotel floors. Leaf structures are represented in the sample
code by Villas and Rooms. These are the actual entities in which you would
spend the night. You cannot really spend the night in a hotel, can you? You
would spend the night in a hotel room.
In the sample, Resorts, Hotels and Floors inherit from the
composite abstract class called CompositeAccomodation. Rooms and Villas inherit
from the Leaf abstract class called Quarters.
The Gof pointed out that there is a hard decision to make in
terms of allowing the Leaf abstract class to fully inherit the Component
abstract class. This is the fact that we should not be able to add components
to a leaf. The design decision made here is to inherit all the methods of the
component class, so that leaves do in fact have a remove-component and add-component
method. In the leaf class we simply throw an exception when these methods are
called. If the client needs to know that it has made a mistake in attempting to
add to a leaf, it can catch these exceptions and further process them. Otherwise,
the attempt to add to the leaf is simply ignored.
The Meat of the Matter
UML – General
Figure 1
UML – Sample Code
Figure 2
Participants – Sample Code
Component - Accomodation
Inherit class defines the behaviors which must be inherited
and overridden in both the composite and leaf participants. It usually includes
behaviors such as add-component and remove-component which add and remove
children component from the said component class. This is an interacting
interface since clients will interact with both composites and leaves posing as
components.
Composite – CompositeAccomodation (ResortArea, Hotel,
Floor)
This is able to add children to it. It aggregates a
datastruture which can keep a reference to multiple child-components. It accepts
instructions from the client to perform operations, but really passes these on
to all of its children one by one (which may pass on these instructions to
their children in cases where they themselves have children). It depends on its
children to do the empirical work.
When thinking about the children of a composite, it is very
important that you not mix up the concept of a child with the concept of a
leaf. A child can be a composite which itself has children.
Leaf – Quarters (HotelRoomSingle, HotelRoomDouble,
Villa)
This is not able to add children to itself. In this code
sample, if child-related operations are attempted, an exception is thrown. It performs
the operations which its parent-component (a composite) has passed on to it. It
may pass results back up to its parent (including passing itself back as a
result) or perform the operation on itself.
Client – Client_Rooms
Client will interact with composites and the leaves they are
“composed” of uniformly, through their component interface. Clients know what
they are adding to or removing from because the client has chosen this direct
interaction. But with regard to the really important aspect of the pattern,
which is the traversal of the tree structure to any depth required to find all
components, the client really does not know the type of class it is dealing
with at any point and does not really care.
Component
The component simply governs behavior. It is a MustInherit
(abstract) class which defines the behaviors that the composites and components
must implement and so defines the behaviors which the client will expect to be
available.
Listing 1
Public MustInherit Class Accomodation 'equivalent of Component in the pattern
Public PossibleNoOccupants As Integer
Public Booked As Boolean
Public AccomodationKey As String
Public AccomodationType As HotelIndustryType
Protected Sub New(ByVal AccName As String, … )
' Constructor
AccomodationKey = AccName
End Sub
' Allows you to add rooms (a child component) to the
' current component.
Public MustOverride Function AddAccomodation
(AccomodationToAdd As Accomodation)
' Allows you to remove rooms (a child component)
' from the current component.
Public MustOverride Function RemoveAccomodation
(ByVal RemoveableAccomodation As Accomodation)
Public Overridable Function CalculateOccupancy
(ByVal Occupancy As Integer) As Long
Dim ActualOccupancy As Integer
Dim PossibleOccupancy As Integer
' This method call is EXTREMELY DECEPTIVE in its simplicity.
' Because of the transparency
' of composites and leaves and the fact that composites
' delegate operations to their child-components so
' transparently. It shows the flexibility, power and
' transparency of the pattern VERY NICELY.
Dim OccupancyFigures As OccupancyObject =
DetermineOccupancyFigures(New OccupancyObject)
ActualOccupancy = OccupancyFigures.OccupiedBeds
PossibleOccupancy = OccupancyFigures.UnOccupiedBeds +
OccupancyFigures.OccupiedBeds
' See the composite and leaf classes for the actual
' (and very elegant) implementation of this function
Public MustOverride Function DetermineOccupancyFigures
(OccupancyFigures As OccupancyObject) As OccupancyObject
' Searches all the children of this component for the first
' unbooked room with the required number of occupants
' passed to it
Public MustOverride Function FindAvailableAccomodation
(Occupancy As Integer) As Accomodation
End Class
Composite
All of the composite’s behavior is inherited from the
component class. But among all these functions, there are two types of
functions which the composite class implements. The first set of functions is
child management function. These functions perform operations on the
datastructure (Hashtable) by adding and removing children from it.
The other class of functions implemented in the composite
class is the operational functions which are domain specific. In this case,
those functions are specific to the hotel industry. The composite is a real
buck passer and simply passes the instruction to the children components which
it holds in its hash table. So, in these functions you will see a lot of
"For Each" statements in these functions as it loops through each
child and allows each child to perform the required operation.
Listing 2
' Equivalent of composite in the pattern
Public MustInherit Class CompositeAccomodation
Inherits Accomodation
Public Sub New(ByVal ComponentName As String, ByVal
ParentNode As TreeNode)
' Constructor
MyBase.New(ComponentName, ParentNode)
End Sub
' Holds a reference to all the child components
Public AccomodationCollection As New Hashtable
Public Overrides Function AddAccomodation
(AccomodationToAdd As Accomodation)
' Adds a child component to itself
AccomodationCollection.Add
(AccomodationToAdd.AccomodationKey,
AccomodationToAdd)
End Function
Public Overloads Overrides Function BookAccomodation
(ByVal BookAble As Accomodation) As Integer
' Be careful with this function. this function does not scan
' the child-accomodations to FIND a suitable accomodation.
' It scans all child accomodations and books ALL of them.
Dim VisitorsBookedFor As Integer
For Each SuitableAccomodation As Accomodation In
Me.AccomodationCollection
VisitorsBookedFor += SuitableAccomodatn.BookAccomodation
Next
End Function
Public Overrides Function DetermineOccupancyFigures
(ByVal OccObject As OccupancyObject) As OccupancyObject
For Each OccupiableAccomodation As Accomodation In
Me.AccomodationCollection
OccupiableAccomodation.DetermineOccupncyFigures(OccObject)
Next
Return OccObject
End Function
' This is one of the actual classes which will be instantiated
' and used as the client. with all the behaviours of a
' composite class.
Public Class HotelFloor
Inherits CompositeAccomodation
Public Sub New(FloorNumber As Integer, ParentNode As TreeNode)
' Initializes all the extra-pattern state
' (they have no bearing on the pattern operation)
MyBase.New(FloorNumber.ToString, ParentNode)
Me.AccomodationType = HotelIndustryType.Floor
Me.DisplayNode.ImageIndex = HotelIndustryType.Floor
End Sub
End Class
Leaf
Child management functions, which are implemented in the
composite class, are simply empty functions which throw exceptions in their
leaf counterparts. Aside from those functions, the leaf simply implements the
domain-specific operations which its composite parent keeps passing to it.
Listing 3
' Equivalent of leaf in the pattern
Public MustInherit Class Quarters
Inherits Accomodation
Public Sub New ( … )
MyBase.New(ComponentName, ParentNode)
Me.PossibleNoOccupants = NoOccupants
End Sub
Public Overrides Function AddAccomodation
(AccomodationToAdd As Accomodation) As Object
Throw New BadChildOpException
(BadChildOpException.Operation.AddChildToChild)
End Function
Public Overloads Overrides Function BookAccomodation
(ByVal RequiredOccupancy As Integer) As Boolean
If Me.Booked = False And Me.PossibleNoOccupants
<= RequiredOccupancy Then
Me.Booked = True
Return True
Else
Return False
End If
End Function
Public Overrides Function FindAvailableAccomodation
(RequiredOccupancy As Integer) As Accomodation
If Not Me.Booked And
Me.PossibleNoOccupants >= RequiredOccupancy Then
Return Me
End If
End Function
Public Overrides Function DetermineOccupancyFigures
(OccupancyFigures As OccupancyObject) As OccupancyObject
If Me.Booked Then
OccupancyFigures.OccupiedBeds += Me.PossibleNoOccupants
Else
OccupancyFigures.UnOccupiedBeds += Me.PossibleNoOccupants
End If
End Function
End Class
' This is one of the actual classes which will be instantiated
' and used with all the behaviours of a leaf class.
Public Class HotelRoomSingle
Inherits Quarters
Public Sub New(RoomNumber As Integer, ParentNode As TreeNode)
MyBase.New(RoomNumber.ToString, ParentNode, 1)
Me.AccomodationType = HotelIndustryType.Villa
Me.DisplayNode.ImageIndex = HotelIndustryType.Villa
End Sub
End Class
Client
The client in this case is the form. Clicking on relevant
menu objects of the context menu will add components and remove them. Clicking
also fires some of the hotel-based functions.
Listing 4
Private Sub Form1_Load( … ) Handles MyBase.Load
Dim Jamaica As New Composite.ResortArea
("Jamaica", tvwHotelRooms)
Dim JamaicanResorts As TreeNode = Jamaica.DisplayNode
Dim OchoRios As New Composite.ResortArea
("OchoRios", JamaicanResorts)
Dim MontegoBay As New Composite.ResortArea
("MontegoBay", JamaicanResorts)
OchoRios.DisplayNode.Tag = OchoRios
MontegoBay.DisplayNode.Tag = MontegoBay
End Sub
Private Function GetCurrentAccomodation() As Accomodation
' General purpose function which takes the currently selected
' node of the Treeview and returns the Accomodation object
' which is tied to it.
' Think of this as a sort of helper function which is
' partially extra-pattern.
Dim ResultingAccomodation As Accomodation =
CType(tvwHotelRooms.SelectedNode.Tag, Accomodation)
Return ResultingAccomodation
End Function
Private Sub mnuBook_Click Handles mnuBook.Click
WIPAccomodation = GetCurrentAccomodation()
WIPAccomodation.BookAccomodation()
End Sub
Private Sub mnuAddHotel_Click Handles mnuAddHotel.Click
WIPAccomodation = GetCurrentAccomodation()
WIPAccomodation.AddAccomodation(New Composite.Hotel
(“NameofHotel"))
End Sub
Private Sub mnuAdd1Room_Click Handles mnuAdd1Room.Click
WIPAccomodation = GetCurrentAccomodation()
WIPAccomodation.AddAccomodation(New Leaf.HotelRoomSingle(…)
End Sub