Understanding the Composite Pattern
page 3 of 9
by David Simmonds
Feedback
Average Rating: 
Views (Total / Last 10 Days): 35433/ 49

Intent seen through O/S & Applications

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

View Entire Article

User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-26 4:19:56 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search