180 likes | 323 Views
Structural Pattern: Decorator. Chapter 4 – Page 83. There are times when the use of subclasses to modify the behavior of individual objects is problematic.
E N D
Structural Pattern: Decorator Chapter 4 – Page 83 There are times when the use of subclasses to modify the behavior of individual objects is problematic. When objects are alike in some fundamental way, but might have a variety of distinctive details, the Decorator Pattern avoids the complexity of having a large number of derived classes. By applying a Decorator class to the base class, and allowing the Decorator class to have the needed derived classes, the base class is kept streamlined, while still affording flexibility when it comes to adding responsibilities to the base class.
The Decorator Pattern Chapter 4 – Page 84 The Component defines the interface for objects that can have responsibilities added to them dynamically. The ConcreteComponent defines an object to which additional responsibilities can be attached. The Decorator maintains a reference to a Component object and defines an interface that conforms to the Component’s interface. Each ConcreteDecorator adds responsibilities to the Component.
Non-Software Decorator Example Chapter 4 – Page 85 The abstract LibraryItem component has two derived classes: the concrete Book component and the concrete Video component. The abstract Decorator class has one derived class: the concrete Borrowable Decorator, which extends the LibraryItem by adding and maintaining a list of borrowers. Regular books and videos have their appropriate attributes, but also have a list of borrowers when decorated as Borrowable.
Software Decorator Example Chapter 4 – Page 86 The AbstractCheckIn component has a derived class: the concrete ActualCheckIn component. The abstract CheckInDecorator class has three derived concrete Decorator subclasses: the AccessibilityCheckIn (for blindness and wheelchairs), the CommunicationCheckIn (for broadband and Internet access), and the FamilyCheckIn (for family size).
Check-In Decorator Pattern VB Code Chapter 4 – Page 87 Special Needs Form Appears to provide opportunity to input special needs of guests. Suitable Accommodation Form Appears to provide list of potential lodgings that meet guest’s special needs.
Code for the Special Needs Form Chapter 4 – Page 88 Public Class frmSpecialNeeds InheritsSystem.Windows.Forms.Form DimFullyDecoratedCheckInAsAbstractCheckIn DimdvSuitableRoomsAsDataView 'This is a test value. Normally this value would have 'been passed to this form from some other form DimGuestDetailsAs String = "G120" Private Sub btnGetSuitableRooms_Click(ByVal sender AsSystem.Object, _ ByVal e AsSystem.EventArgs) HandlesbtnGetSuitableRooms.Click FullyDecoratedCheckIn = GetDecorators() DimPreDecoratorAs New PreDecorationObject dvSuitableRooms = FullyDecoratedCheckIn.GetSuitableRooms(PreDecorator) DimSuitableAccomodationsFormAs New frmSuitableAccomodation SuitableAccomodationsForm.GuestBookableRooms = dvSuitableRooms SuitableAccomodationsForm.GuestNumberToCheckIn = GuestDetails SuitableAccomodationsForm.Show() End Sub Public Function GetDecorators() AsAbstractCheckIn DimFullyDecoratedCheckInAsAbstractCheckIn DimAccessibilityRequirementsAs String = "" DimCommunicationNeedsAs String = "" DimFamilySizeAs String = "" DimCheckInDecoratorFactoryAs New DecoratorFactory DimCheckInToDecorateAsAbstractCheckIn = NewActualCheckIn
For Each AccessibilityChoiceAsCheckBoxIn Me.gbxAccessibility.Controls IfAccessibilityChoice.CheckedThen IfAccessibilityRequirements <> ""Then AccessibilityRequirements &= " , " End If AccessibilityRequirements &= AccessibilityChoice.Text End If Next For EachChildrenChoiceNoAsRadioButtonIn Me.gbxChildren.Controls IfChildrenChoiceNo.CheckedThen FamilySize = ChildrenChoiceNo.Text Exit For End If Next For Each CommunicationChoiceAsRadioButtonIn Me.gbxCommunications.Controls IfCommunicationChoice.CheckedThen CommunicationNeeds = CommunicationChoice.Text Exit For End If Next Dim RequirementsArray() As String = _ {AccessibilityRequirements, CommunicationNeeds, FamilySize} FullyDecoratedCheckIn = CheckInDecoratorFactory.GetFullyDecoratedCheckin(CheckInToDecorate, RequirementsArray) ReturnFullyDecoratedCheckIn End Function Private Sub frmSpecialNeeds_Load(ByVal sender AsSystem.Object, _ ByVal e AsSystem.EventArgs) HandlesMyBase.Load Me.txtGuestDetails.Text = Me.GuestDetails End Sub End Class Chapter 4 – Page 89
Code for the Accommodation Form Chapter 4 – Page 90 ImportsSystem.Data.OleDb Public Class frmSuitableAccomodation InheritsSystem.Windows.Forms.Form PublicGuestNumberToCheckInAs String PublicGuestBookableRoomsAsDataView Private Sub btnCheckInGuest_Click(ByVal sender AsSystem.Object, _ ByVal e AsSystem.EventArgs) HandlesbtnCheckInGuest.Click ' Logic for inserting the Guest number passed to the form into the database ' as the guest who should be assigned to the room which is checked End Sub Private Sub frmSuitableAccomodation_Load(ByVal sender AsSystem.Object, _ ByVal e AsSystem.EventArgs) HandlesMyBase.Load Me.dgrGuestsAndRooms.DataSource = Me.GuestBookableRooms End Sub End Class
Code for the “Decorator Factory” Chapter 4 – Page 91 Public Class DecoratorFactory 'This is a sort of "Decorator-Factory", calling in the 'functions in order of inner-decorator to outer-decorator. PublicPartiallyDecoratedCheckinAsAbstractCheckIn PublicAccessibilityGroupBoxAsGroupBox PublicCommunicationsGroupBoxAsGroupBox PublicchildrenGroupBoxAsGroupBox Public Function GetFullyDecoratedCheckin(ByValUndecoratedCheckinAsAbstractCheckIn, _ ByValDecorationParameters() As String) As AbstractCheckIn DimFullyDecoratedCheckinAsAbstractCheckIn ' This is where we decide the order in which to wrap the decorators. If the order is very ' important, then we brainstorm with the subject experts once, figure out how to wrap ' them once, decide once and all clients which call the factory will benefit from the ' stantardized decisions we have made so the possibility of getting it wrong is reduced. PartiallyDecoratedCheckin = _ GetAccessibilityDecorator(UndecoratedCheckin, DecorationParameters(0)) PartiallyDecoratedCheckin = _ GetCommunicationDecorator(PartiallyDecoratedCheckin, DecorationParameters(1)) FullyDecoratedCheckin = _ GetFamilyDecorator(PartiallyDecoratedCheckin, DecorationParameters(2)) ReturnFullyDecoratedCheckin End Function
Private Function GetAccessibilityDecorator(ByValComponentToDecorateAsAbstractCheckIn, _ ByValAccessibilityStringAs String) AsAbstractCheckIn DimDecoratoredCheckInAsAbstractCheckIn IfAccessibilityString <> "" Then DecoratoredCheckIn = _ NewAccessibilityRequirementsDecorator(ComponentToDecorate, AccessibilityString) Else DecoratoredCheckIn = ComponentToDecorate' We return the object undecorated End If Return DecoratoredCheckIn End Function Private Function GetFamilyDecorator(ByValComponentToDecorateAsAbstractCheckIn, _ ByValFamilyStringAs String) AsAbstractCheckIn DimDecoratoredCheckInAsAbstractCheckIn 'The following if statement decides whether to wrap the component which was passed to the ' function (which may itself be a component or a decorated component) in a decorator ' and pass it back, or simply return the undecorated parameter back to the caller. IfFamilyString <> "None" Then DecoratoredCheckIn = NewFamilyRequirementsDecorator(ComponentToDecorate, FamilyString) Else DecoratoredCheckIn = ComponentToDecorate' We return the object undecorated End If Return DecoratoredCheckIn End Function Chapter 4 – Page 92
Chapter 4 – Page 93 Private Function GetCommunicationDecorator(ByValComponentToDecorateAsAbstractCheckIn, _ ByValCommunicationStringAs String) AsAbstractCheckIn DimDecoratoredCheckInAsAbstractCheckIn IfCommunicationString <> "None" Then DecoratoredCheckIn = _ NewCommunicationRequirementsDecorator(ComponentToDecorate, CommunicationString) Else DecoratoredCheckIn = ComponentToDecorate' We return the object undecorated End If Return DecoratoredCheckIn End Function End Class
Decorator and Component Code Chapter 4 – Page 94 ImportsSystem.Data.OleDb Public Interface AbstractCheckIn' Abstract Component FunctionGetSuitableRooms(ByValDecorationObjectAsPreDecorationObject) AsDataView End Interface Public Class ActualCheckIn' Concrete Component ImplementsAbstractCheckIn Public Function GetSuitableRooms(ByValDecorationObjectAsPreDecorationObject) _ AsDataViewImplementsAbstractCheckIn.GetSuitableRooms DimstrRoomsConnectionAs String 'Note: Ensure that the database path and name used in this connection string are correct. strRoomsConnection = _ "Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin;Data Source=C:\Decorator_CheckIn.Mdb" DimconnRoomsAs New OleDbConnection(strRoomsConnection) connRooms.Open() DimstrRoomsSQLAs String = "Select * from rooms " DimstrSQLExtensionAs String = DecorationObject.WhereClause DimcmdRoomsAs New OleDbCommand(strRoomsSQL & strSQLExtension, connRooms) DimdaRoomsAs New OleDbDataAdapter(cmdRooms) DimdtRoomsAs New DataTable daRooms.Fill(dtRooms) DimdvRoomsAs New DataView(dtRooms) ReturndvRooms End Function End Class
Chapter 4 – Page 95 Public Class CheckInDecorator' Abstract Decorator ImplementsAbstractCheckIn 'Because it holds a reference to an abstract class, it doesn't know until ' runtime whether it is another decorator like itself or a concrete ' component. This allows for recursive composure to any depth required. Privatem_ActualCheckInAsAbstractCheckIn Private Sub New() End Sub Public Sub New(ByValCheckInAsAbstractCheckIn) m_ActualCheckIn = CheckIn End Sub PublicReadOnly Property ActualCheckin() AsAbstractCheckIn Get Return m_ActualCheckIn End Get End Property PublicOverridable Function GetSuitableRooms(ByValDecorationObjectAsPreDecorationObject) _ AsDataViewImplementsAbstractCheckIn.GetSuitableRooms End Function End Class
Chapter 4 – Page 96 Public ClassAccessibilityRequirementsDecorator InheritsCheckInDecorator DimAccessibilityNeedsAs String Public Sub New(ByValCheckOpAsAbstractCheckIn, ByValAccessNeedsAs String) MyBase.new(CheckOp) AccessibilityNeeds = AccessNeeds End Sub Public Overrides Function GetSuitableRooms(ByValDecorationObject _ AsPreDecorationObject) AsDataView ' On the way in, intercept the SQLs and finetune them more IfDecorationObject.WhereClause = ""Then DecorationObject.WhereClause += " where “ Else DecorationObject.WhereClause += " and “ End If DecorationObject.WhereClause += " ExitRoom = 'True'" ' On the way back out, intercept the dataset and make it ' more useful by sorting on the Accessibility issues Dim FinalDataView As DataView = Me.ActualCheckin.GetSuitableRooms(DecorationObject) FinalDataView.Sort = AccessibilityNeeds ReturnFinalDataView End Function End Class
Chapter 4 – Page 97 Public ClassCommunicationRequirementsDecorator InheritsCheckInDecorator DimCommunicationNeedsAs String Public Sub New(ByValCheckOpAsAbstractCheckIn, ByValCommNeedsAs String) MyBase.new(CheckOp) CommunicationNeeds = CommNeeds End Sub Public Overrides Function GetSuitableRooms(ByValDecorationObject _ AsPreDecorationObject) AsDataView IfDecorationObject.WhereClause = ""Then DecorationObject.WhereClause += " where " Else DecorationObject.WhereClause += " and " End If DecorationObject.WhereClause += " CommunicationsSetup = '" & CommunicationNeeds & "'" Return Me.ActualCheckin.GetSuitableRooms(DecorationObject) End Function End Class
Chapter 4 – Page 98 Public ClassFamilyRequirementsDecorator' Concrete Decorator InheritsCheckInDecorator DimNumberofChildrenAs Integer Public Sub New(ByValCheckOpAsAbstractCheckIn, ByValNumberChildrenInWordsAs String) MyBase.new(CheckOp) NumberofChildren = ConvertFromStringToNumber(NumberChildrenInWords) End Sub FunctionConvertFromStringToNumber(ByValNumberAsWordsAs String) As Integer Select Case LCase(NumberofChildren) Case"one" NumberofChildren = 1 Case"one" NumberofChildren = 2 End Select End Function Public Overrides Function GetSuitableRooms(ByValDecorationObject _ AsPreDecorationObject) AsDataView Select Case Me.NumberofChildren Case 1 DecorationObject.WhereClause += " and ChildAmenities = 'Medium'" Case 2 DecorationObject.WhereClause += " and ChildAmenities = 'High'" End Select Return Me.ActualCheckin.GetSuitableRooms(DecorationObject) End Function End Class
Chapter 4 – Page 99 Public ClassPreDecorationObject PublicColumnsClauseAs String PublicWhereClauseAs String Public Sub New() Me.ColumnsClause = "" Me.WhereClause = "" End Sub End Class
Decorator Pattern Advantages Chapter 4 – Page 100 • The Decorator Pattern provides a more flexible way to add responsibilities to a class than the use of inheritance, since it can add these responsibilities to selected instances of the class. • The Decorator Pattern also allows you to customize a class without creating subclasses high in the inheritance hierarchy. • One disadvantage of this pattern is that the Decorator and its enclosed Component are not identical, so tests for object type will fail. • Another problem is that this pattern tends to produce a system with several very similar objects, which can lead to confusion for the programmer maintaining the code.