structural pattern: decorator
DESCRIPTION
Structural Pattern: Decorator. Chapter 4 – Page 83. There are times when the use of subclasses to modify the behavior of individual objects is problematic. - PowerPoint PPT PresentationTRANSCRIPT
Structural Pattern: DecoratorThere are times when the use of subclasses to modify the behavior of individual objects is problematic.
Chapter 4 – Page 1
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 PatternChapter 4 – Page 2The 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.
ConcreteComponent
Operation()
ConcreteDecoratorAAddedState
Operation()
ConcreteDecoratorB
Operation()AddedBehavior()
Decorator
Operation()
Component
Operation()
+component
Operation() component.Operation()
Operation() base.Operation(); AddedBehavior();
Non-Software Decorator ExampleThe abstract LibraryItem component has two derived classes: the concrete Book component and the concrete Video component.
Chapter 4 – Page 3
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.
VideoDirectorTitlePlayTime
Display()
BorrowableBorrowers[]
Display()BorrowItem()ReturnItem()
Decorator
Display()
LibraryItemNumberOfCopies
Display()
BookAuthorTitle
Display()
Software Decorator ExampleThe AbstractCheckIn component has a derived class: the concrete ActualCheckIn component.
Chapter 4 – Page 4
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).
ActualCheckIn
GetSuitableRooms()
AccessibilityCheckInAccessibilityNeeds
GetSuitableRooms()
CommunicationCheckInCommunicationNeeds
GetSuitableRooms()
AbstractCheck In
GetSuitableRooms()
CheckInDecorator
GetSuitableRooms()
FamilyCheckInFamilySize
GetSuitableRooms()
Check-In Decorator Pattern VB CodeChapter 4 – Page 5
Special Needs FormAppears to provide opportunity to input special needs of guests.
Suitable Accommodation
FormAppears to provide
list of potential lodgings that meet
guest’s special needs.
Chapter 4 – Page 6
Public Class frmSpecialNeeds Inherits System.Windows.Forms.Form
Dim FullyDecoratedCheckIn As AbstractCheckIn Dim dvSuitableRooms As DataView
'This is a test value. Normally this value would have 'been passed to this form from some other form Dim GuestDetails As String = "G120"
Private Sub btnGetSuitableRooms_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnGetSuitableRooms.Click
FullyDecoratedCheckIn = GetDecorators() Dim PreDecorator As New PreDecorationObject dvSuitableRooms = FullyDecoratedCheckIn.GetSuitableRooms(PreDecorator)
Dim SuitableAccomodationsForm As New frmSuitableAccomodation SuitableAccomodationsForm.GuestBookableRooms = dvSuitableRooms SuitableAccomodationsForm.GuestNumberToCheckIn = GuestDetails SuitableAccomodationsForm.Show() End Sub
Public Function GetDecorators() As AbstractCheckIn Dim FullyDecoratedCheckIn As AbstractCheckIn
Dim AccessibilityRequirements As String = "" Dim CommunicationNeeds As String = "" Dim FamilySize As String = ""
Dim CheckInDecoratorFactory As New DecoratorFactory Dim CheckInToDecorate As AbstractCheckIn = New ActualCheckIn
Code for the Special Needs Form
Chapter 4 – Page 7 For Each AccessibilityChoice As CheckBox In Me.gbxAccessibility.Controls If AccessibilityChoice.Checked Then If AccessibilityRequirements <> "" Then AccessibilityRequirements &= " , " End If AccessibilityRequirements &= AccessibilityChoice.Text End If Next
For Each ChildrenChoiceNo As RadioButton In Me.gbxChildren.Controls If ChildrenChoiceNo.Checked Then FamilySize = ChildrenChoiceNo.Text Exit For End If Next
For Each CommunicationChoice As RadioButton In Me.gbxCommunications.Controls If CommunicationChoice.Checked Then CommunicationNeeds = CommunicationChoice.Text Exit For End If Next
Dim RequirementsArray() As String = _ {AccessibilityRequirements, CommunicationNeeds, FamilySize}
FullyDecoratedCheckIn = CheckInDecoratorFactory.GetFullyDecoratedCheckin(CheckInToDecorate, RequirementsArray) Return FullyDecoratedCheckIn
End Function
Private Sub frmSpecialNeeds_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Me.txtGuestDetails.Text = Me.GuestDetails End SubEnd Class
Chapter 4 – Page 8
Imports System.Data.OleDb
Public Class frmSuitableAccomodation Inherits System.Windows.Forms.Form
Public GuestNumberToCheckIn As String Public GuestBookableRooms As DataView
Private Sub btnCheckInGuest_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCheckInGuest.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 As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load
Me.dgrGuestsAndRooms.DataSource = Me.GuestBookableRooms
End SubEnd Class
Code for the Accommodation Form
Chapter 4 – Page 9Public Class DecoratorFactory
'This is a sort of "Decorator-Factory", calling in the 'functions in order of inner-decorator to outer-decorator. Public PartiallyDecoratedCheckin As AbstractCheckIn
Public AccessibilityGroupBox As GroupBox Public CommunicationsGroupBox As GroupBox Public childrenGroupBox As GroupBox
Public Function GetFullyDecoratedCheckin(ByVal UndecoratedCheckin As AbstractCheckIn, _ ByVal DecorationParameters() As String) As AbstractCheckIn Dim FullyDecoratedCheckin As AbstractCheckIn
' 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))
Return FullyDecoratedCheckin End Function
Code for the “Decorator Factory”
Chapter 4 – Page 10 Private Function GetAccessibilityDecorator(ByVal ComponentToDecorate As AbstractCheckIn, _ ByVal AccessibilityString As String) As AbstractCheckIn
Dim DecoratoredCheckIn As AbstractCheckIn
If AccessibilityString <> "" Then DecoratoredCheckIn = _ New AccessibilityRequirementsDecorator(ComponentToDecorate, AccessibilityString) Else DecoratoredCheckIn = ComponentToDecorate ' We return the object undecorated End If Return DecoratoredCheckIn
End Function
Private Function GetFamilyDecorator(ByVal ComponentToDecorate As AbstractCheckIn, _ ByVal FamilyString As String) As AbstractCheckIn
Dim DecoratoredCheckIn As AbstractCheckIn
'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. If FamilyString <> "None" Then DecoratoredCheckIn = New FamilyRequirementsDecorator(ComponentToDecorate, FamilyString) Else DecoratoredCheckIn = ComponentToDecorate ' We return the object undecorated End If Return DecoratoredCheckIn
End Function
Chapter 4 – Page 11 Private Function GetCommunicationDecorator(ByVal ComponentToDecorate As AbstractCheckIn, _ ByVal CommunicationString As String) As AbstractCheckIn
Dim DecoratoredCheckIn As AbstractCheckIn
If CommunicationString <> "None" Then DecoratoredCheckIn = _ New CommunicationRequirementsDecorator(ComponentToDecorate, CommunicationString) Else DecoratoredCheckIn = ComponentToDecorate ' We return the object undecorated End If Return DecoratoredCheckIn
End FunctionEnd Class
Chapter 4 – Page 12Imports System.Data.OleDb
Public Interface AbstractCheckIn ' Abstract Component Function GetSuitableRooms(ByVal DecorationObject As PreDecorationObject) As DataView
End Interface
Public Class ActualCheckIn ' Concrete Component Implements AbstractCheckIn
Public Function GetSuitableRooms(ByVal DecorationObject As PreDecorationObject) _ As DataView Implements AbstractCheckIn.GetSuitableRooms
Dim strRoomsConnection As 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" Dim connRooms As New OleDbConnection(strRoomsConnection) connRooms.Open()
Dim strRoomsSQL As String = "Select * from rooms " Dim strSQLExtension As String = DecorationObject.WhereClause Dim cmdRooms As New OleDbCommand(strRoomsSQL & strSQLExtension, connRooms) Dim daRooms As New OleDbDataAdapter(cmdRooms) Dim dtRooms As New DataTable daRooms.Fill(dtRooms) Dim dvRooms As New DataView(dtRooms) Return dvRooms
End Function
End Class
Decorator and Component Code
Chapter 4 – Page 13Public Class CheckInDecorator ' Abstract Decorator Implements AbstractCheckIn
'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.
Private m_ActualCheckIn As AbstractCheckIn
Private Sub New() End Sub
Public Sub New(ByVal CheckIn As AbstractCheckIn) m_ActualCheckIn = CheckIn End Sub
Public ReadOnly Property ActualCheckin() As AbstractCheckIn Get Return m_ActualCheckIn End Get End Property
Public Overridable Function GetSuitableRooms(ByVal DecorationObject As PreDecorationObject) _ As DataView Implements AbstractCheckIn.GetSuitableRooms
End FunctionEnd Class
Chapter 4 – Page 14
Public Class AccessibilityRequirementsDecorator Inherits CheckInDecorator
Dim AccessibilityNeeds As String
Public Sub New(ByVal CheckOp As AbstractCheckIn, ByVal AccessNeeds As String) MyBase.new(CheckOp) AccessibilityNeeds = AccessNeeds End Sub
Public Overrides Function GetSuitableRooms(ByVal DecorationObject _ As PreDecorationObject) As DataView
' On the way in, intercept the SQLs and finetune them more If DecorationObject.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 Return FinalDataView
End Function
End Class
Chapter 4 – Page 15Public Class CommunicationRequirementsDecorator Inherits CheckInDecorator
Dim CommunicationNeeds As String
Public Sub New(ByVal CheckOp As AbstractCheckIn, ByVal CommNeeds As String) MyBase.new(CheckOp) CommunicationNeeds = CommNeeds End Sub
Public Overrides Function GetSuitableRooms(ByVal DecorationObject _ As PreDecorationObject) As DataView
If DecorationObject.WhereClause = "" Then DecorationObject.WhereClause += " where " Else DecorationObject.WhereClause += " and " End If DecorationObject.WhereClause += " CommunicationsSetup = '" & CommunicationNeeds & "'"
Return Me.ActualCheckin.GetSuitableRooms(DecorationObject)
End FunctionEnd Class
Chapter 4 – Page 16Public Class FamilyRequirementsDecorator ' Concrete Decorator Inherits CheckInDecorator
Dim NumberofChildren As Integer
Public Sub New(ByVal CheckOp As AbstractCheckIn, ByVal NumberChildrenInWords As String) MyBase.new(CheckOp) NumberofChildren = ConvertFromStringToNumber(NumberChildrenInWords) End Sub
Function ConvertFromStringToNumber(ByVal NumberAsWords As String) As Integer Select Case LCase(NumberofChildren) Case "one" NumberofChildren = 1 Case "one" NumberofChildren = 2 End Select
End Function
Public Overrides Function GetSuitableRooms(ByVal DecorationObject _ As PreDecorationObject) As DataView
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 FunctionEnd Class
Chapter 4 – Page 17
Public Class PreDecorationObject Public ColumnsClause As String Public WhereClause As String
Public Sub New() Me.ColumnsClause = "" Me.WhereClause = "" End Sub
End Class
Decorator Pattern AdvantagesChapter 4 – Page 18
• 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.
• One disadvantage of this pattern is that the Decorator and its enclosed Component are not identical, so tests for object type will fail.
• The Decorator Pattern also allows you to customize a class without creating subclasses high in the inheritance hierarchy.
• 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.