This will instantiate a default Abstractor (in this case a
VerbalEvaluation). It also configures the default renderer with a default Implementer
(in this case a Fish renderer). So the form is ready to go as soon as it
loads.
When the user clicks on an evaluation control (such as the track
bar, numericupdown or list box) the class which wraps that control becomes
instantiated by the client. That abstractor will now be responsible for
interpreting user inputs and converting them into abstractions (the score). In
order to fulfill the intent of the pattern, the form maintains a reference to
the Abstraction class AND the Implementer class. This means that whenever we
destroy one or the other, we can easily reconfigure the new class with the
existing one. The user never has to do two selections before seeing a
rendition of the rating.
We also perform a little trick when we are changing an implementer
class. In order to clear the picture box which is becoming inactive, we allow
the form to send an instruction to the implementer to render a “0” score (which
clears the graphic surface). This allows us to display just one graphical rendition
of the score at a time. This is done purely for aesthetic reasons.
Now ideally, we would also have a routine which disables
controls which are not active, so we would see only one evaluation at a time. However,
time does not permit this. The reader could attempt this implementation if
they so desire.
Listing 3
Private Sub frmRatingShell_Load(...)Handles MyBase.Load
Me.RenderingImplementor = New
Implementors.FishRenderer(Me.pbxFishes, Me.imlFish)
Me.AbstractEvaluator = New Abstractors.VerbalEvaluation
( Me.VerbalEval, Me.RenderingImplementor)
End Sub
Private Sub SlidingBar_Scroll(…) _
Handles SlidingBar.Scroll
ChangeEvaluator("Sliding")
AbstractEvaluator.UpdateScoreValue()
End Sub
Private Sub NumericEval_ValueChanged(…) _
Handles NumericEval.ValueChanged
ChangeEvaluator("Numeric")
AbstractEvaluator.UpdateScoreValue()
End Sub
Private Sub pbxFaces_Click( …) Handles pbxFaces.Click
ChangeRenderer("Faces")
End Sub
Private Sub pbxFishes_Click( … ) Handles pbxFishes.Click
ChangeRenderer("Fishes")
End Sub
Public Function ChangeEvaluator(ByVal EvaluatorType As String)
' This is a makeshift “factory-function” which instantiates
' the Abstractors for us
' We could just as easily have used Prototype Pattern
Me.AbstractEvaluator = Nothing
Select Case EvaluatorType
Case "Verbal"
AbstractEvaluator = New Abstractors.VerbalEvaluation
(VerbalEval, RenderingImplementor)
Case "Sliding"
AbstractEvaluator = New Abstractors.SlidingEvaluation _
(SlidingBar, RenderingImplementor)
Case "Numeric"
AbstractEvaluator = New Abstractors.NumericEvaluation
(NumericEval, RenderingImplementor)
End Select
AbstractEvaluator.UpdateScoreValue()
End Function
Public Function ChangeRenderer(ByVal RendererType As String)
' Clears the graphic surface of the Implementor we are
' decomissioning
Me.RenderingImplementor.RenderRating(0)
Me.RenderingImplementor = Nothing
' This statement-block figures out which implementor to
' instantiate.
' It assigns the new implementor to the Parent container
' which is the form
' so that even when we destroy the Abstractor,
' the implementor is kept alive
' and we can commission it back into use by assigning it to
' the new Abstractor
Select Case RendererType
Case "Faces"
Me.RenderingImplementor = New Implementors.FaceRenderer
(Me.pbxFaces, Me.imlFace)
Case "Stars"
Me.RenderingImplementor = New Implementors.StarRenderer
(Me.pbxStars, Me.ImlStar)
End Select
' Takes the reference to the implementor which the Form keeps
' and assigns it to the new Abstractor
Me.AbstractEvaluator.RatingRenderDevice = _
Me.RenderingImplementor
AbstractEvaluator.UpdateScoreValue()
End Function
End Class
Opportunities for and Costs of - Adaptation and
Extension
Adaptation is easy in the bridge pattern. Modification of a
refined abstraction or a concrete implementation requires absolutely no testing
of the other classes in the system. That is unless you do something like (in
this case) changing the rating scale which the class uses. This would force
you to revise all the classes throughout the system. That is more than just a
change to a class, right? If you look at the pattern tweaking section, you
will see a discussion on the expansion of the bridge pattern which includes the
incorporation of a signal class and the way in which it ensures that
programmers do not mistakenly change the outputs of refined abstractions or the
inputs to concrete implementers in an arbitrary manner in the course of
maintaining the abstraction/implementation classes.
So unexpected blunders aside, we can maintain abstractions
and implementations in a very discrete way. The same way we could change
almost any wiper-blade in a wiper without affecting the wiper motor (another
example of the bridge system in real life).
In the same vein, we can add more abstractions or
implementations without affecting the others in the system. There is nothing
we have to do to register new abstractions/implementations either; we simply
make use of them in the client. The client simply instantiates the new
implementation and configures a new abstraction with it, calling the relevant
operation on the abstraction to do the work required.