Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Using Composition
Main concepts
Chapter contents
The previous two chapters have introduced us to the concepts of association, composition
and delegation. Briefly stated, we write a new class that refers to or incorporates an instance
of another class, in the sense of carrying a link to it, and then calls its methods to use some
or all of that class’s functionality without having to re-implement it.
By allowing one to incorporate the facilities of an existing class into a new class,
delegation has some similarity to inheritance. However, unlike inheritance, each required
facility must be exposed explicitly. While this requires more work on the part of the
programmer, it also allows facilities of the existing class to be kept hidden, something not
possible through inheritance.
If a single class delegates operations to more than one existing class, an effect similar to
multiple inheritance is possible while avoiding some of the complications.
Delegation is an important OO principle and is used in a variety of patterns such as the
Adapter, the Facade and the Strategy patterns. In this chapter we look at the Adapter and
Facade patterns.
Chapter 10, Page 2 Object orientation with Delphi (all rights reserved)
Example 10.1 The Adapter Pattern
A composed object is the owner of constituent objects that it can use while hiding them from
others. This opens up the possibility of adapting legacy software or existing classes for
reuse. When looking for reuse of existing software in a new application we may find a class
or a non-OO program that performs the operations we need but that does not meet some
other requirement or that may be subject to future change. Adapting accommodates these
factors by encapsulating this existing software as an object and limiting its coupling with the
rest of the program.
This example presents the Adapter Pattern as a way of reusing legacy software within an
OO programming approach. The next example shows how an object in one hierarchy may
be adapted to another inheritance hierarchy when this is required for polymorphism.
Assume that we are programming to a standard that requires separate access methods for
modifying and for reading object data, and that these methods must work with a consistent
data type. We have an existing TCounter class that does not meet these standards. First, its
access methods (the methods AddAndRead and ClearAndRead below) both modify the
value of the field data and return a value, which is not good programming practice. Second,
its access methods accept an integer and return a string. According to the standard these
should be of the same type. We want to reuse this class but we don’t want to ignore our
standards, so we can create an adapter with the required interface. The existing class is:
1 unit CounterU;
2 interface
3 type
4 TCounter = class (TObject)
5 protected // allows subclasses to access this data field
6 Total: Integer;
7 public
8 function AddAndRead (Number: Integer): string;
9 function ClearAndRead: string;
10 end; // end TCounter = class (TObject)
11 implementation
12
13 uses
14 SysUtils;
We create a composed class that has the method signatures required by the standards (the
methods Add, Clear and GetValue) and that adapts these to calls to the legacy class:
1 unit NewCounterU;
2 interface
3 uses
4 CounterU; // contains the adapted class
5 type
6 TNewCounter = class(TObject)
7 private
8 FOldCounter: TCounter; // the adaptee, kept private
9 public
10 // composition: must propagate create and destroy
11 constructor Create;
12 destructor Destroy; override;
13 // adapter methods providing the required interface
14 procedure Add (ANumber: integer);
15 procedure Clear;
16 function GetValue: integer;
17 end; // end TNewCounter = class(TObject)
18 implementation
19 uses SysUtils;
20 { TNewCounter }
Chapter 10, Page 4 Object orientation with Delphi (all rights reserved)
25 procedure TNewCounter.Clear;
26 begin
27 FOldCounter.ClearAndRead; // ignore the return value
28 end; // end procedure TNewCounter.Clear
29 constructor TNewCounter.Create;
30 begin
31 inherited;
32 FOldCounter := TCounter.Create; // propagate
33 end; // end constructor TNewCounter.Create
34 destructor TNewCounter.Destroy;
35 begin
36 FOldCounter.Free; // propagate
37 inherited;
38 end; // end destructor TNewCounter.Destroy
We now have two procedure methods for manipulating the data (lines 21–28) and a function
for getting the value (lines 39–42), and so comply with Delphi programming convention as
well as the more general convention that separate methods be used for reading and writing
object data. Add and GetValue also both involve an integer. (GetValue uses StrToInt for the
conversion.)
The Adapter Pattern relies on composition, and so this class uses an explicit constructor
and explicit destructor to propagate object creation and destruction (lines 29–38).
1 unit AdapterDemoU;
2 interface
21 implementation
22 uses NewCounterU;
23 var
24 Items: TNewCounter; // the adapter
25 {$R *.DFM}
36 initialization
37 Items := TNewCounter.Create;
This program works well. It uses TNewCounter, the adapter class (lines 22, 24), and does
not know that these method calls are delegated to an instance of TCounter. Thus
encapsulation is maintained and all the coupling is between neighbouring classes. As
suggested by the Law of Demeter, TfrmItems knows only about TNewCounter, which in
turn knows only about TCounter. TCounter knows about either of the other classes (figure
3).
Chapter 10, Page 6 Object orientation with Delphi (all rights reserved)
Figure 3 TNewCounter as an object adapter for TCounter
The class given in step 2 is a simple adapter. It stores no local data and performs no local
manipulations. This is the goal of the Adapter Pattern, but it can lead to clumsiness and/or
inefficiency. For example, the Get... method above requires a clumsy implementation (step
2, line 41). Assuming that we can’t modify the legacy class (the adaptee, TCounter) we may
decide to create a local variable in the adapter class to store the value locally.
There are also situations where the adapted class does not provide all of the required
functionality, and then we need to implement these in the adapter. For example, in keeping
with the previous chapter, this adapter should have an Assign method. So this new version
of the adapter is derived from TPersistent. (For brevity, we won’t implement the AssignTo
here.)
1 unit NewCounterU;
2 interface
4 type
5 TNewCounter = class(TPersistent)
6 private
7 FOldCounter: TCounter; // the adaptee
8 FValue: string; // data local to the adapter
9 public
10 procedure Assign (ANewCounter: TPersistent); override;
11 constructor Create;
12 destructor Destroy; override;
13 // adapter methods
14 procedure Add (ANumber: integer);
15 procedure Clear;
16 function GetValue: integer;
17 end; // end TNewCounter = class(TPersistent)
18 implementation
19 uses SysUtils;
32 procedure TNewCounter.Clear;
33 begin
34 FValue := FOldCounter.ClearAndRead;
35 end; // end procedure TNewCounter.Clear
36 constructor TNewCounter.Create;
37 begin
38 inherited;
39 FOldCounter := TCounter.Create; // propagate
40 end; // end constructor TNewCounter.Create
41 destructor TNewCounter.Destroy;
42 begin
43 FOldCounter.Free; // propagate
44 inherited;
45 end; // end destructor TNewCounter.Destroy
We declare the local variable in line 8, and set it in lines 23 and 34. Then in the Get... method
we simply read the local variable (line 48). In the next step we’ll change the user interface to
test the Assign method.
Chapter 10, Page 8 Object orientation with Delphi (all rights reserved)
Ex 10.1 step 5 Testing the Assign method
To test the Assign method, add two buttons, called btnCopy and btnShowCopy, to the
existing user interface (figure 4), declare an additional reference ItemsCopy to a
TNewCounter and add the buttons’ OnClick event handlers and the instantiation for
ItemsCopy:
initialization
Items := TNewCounter.Create;
ItemsCopy := TNewCounter.Create; // new
1 unit TheCupboardU;
2 interface
3 type
4 TTheCupboard = class (TObject)
5 public
6 function HuRU: string;
7 end; // TTheCupboard = class (TObject)
8 implementation
9 { TTheCupboard }
Two factors prevent us from using this code as it is in the program of example 6.3. It’s not
part of the TFurniture hierarchy so it can’t participate in the polymorphic method call
Chapter 10, Page 10 Object orientation with Delphi (all rights reserved)
(example 6.3 step 2, line 39), and the method has the wrong name (HuRU, which some crazy
programmer thought was a cool abbreviation for ‘Who are you?’, instead of GetKind).
The adapter pattern suggests that we create a composed class, TCupboard within the existing
TFurniture hierarchy, to delegate its operations to the consituent TTheCupboard class
shown above, which is not part of the TFurniture hierarchy.
Since all the classes in the TFurniture hierarchy are very small, we have placed them all
in the same unit. With more complex classes they would be in separate units.
1 unit FurnitureU;
2 // A polymorphic adapter
3 interface
4 uses
5 TheCupboardU; // unit containing the adaptee
6 type
35 implementation
61 { TCupboard }
62 constructor TCupboard.Create;
63 begin
64 inherited;
65 FOtherCupboard := TTheCupboard.Create;
66 end; // end constructor TCupboard.Create
67 destructor TCupboard.Destroy;
68 begin
69 FOtherCupboard.Free;
70 inherited;
71 end; // end destructor TCupboard.Destroy
As always with a composed class, we propagate Create (lines 62–66) and Destroy (lines
67–71). Delegating the responsibility is a breeze (line 74) and allows us to adapt the method
name as well. Notice that the delegating method is still declared as an override method, like
the other GetKind methods (line 33). We don’t implement an Assign method because
TFurniture has no data fields.
Because we have been able to maintain the polymorphism through the adapter, including
the cupboard in our driver program is simple. We add it as an Item after Chair in the
ComboBox list and then add a line to the Case statement in the OnClick event handler (line
30), adjusting the other Case indexes as necessary (cf example 6.3, step 2, lines 24–36). We
need make no change to btnKind’s OnClick event handler (example 6.3, step 2, lines 37–43).
Because we reset the MyFurniture reference in line 26, we don’t need an else clause in the
case statement to catch invalid values of ItemIndex.
Chapter 10, Page 12 Object orientation with Delphi (all rights reserved)
33 5: MyFurniture := TKitchenTable.Create;
34 end;
35 lblKind.Caption := '';
36 end; // end procedure TfrmSubstitution.rgbFurnitureClick
Here we see that an adapter class can be used polymorphically within its inheritance
hierarchy. The adaptee (the adapted class) does not operate polymorphically through its
inheritance hierarchy when accessed via the adapter.
The adapter in the examples above is an object adapter, and is implemented through
composition. Sometimes, and more often in languages like C++ that allow multiple
inheritance, one also sees a class adapter, implemented through inheritance.
In view of the cautions raised in chapter 7 about the appropriate use of inheritance, we
generally recommend object adapters in preference to class adapters. But some important
authors, including the Gang of Four (Gamma et al, 1995) discuss class adapters and so we do
too.
If we have an existing class with the required functionality but with the wrong signature,
we can derive an adapter class from the existing class and implement the appropriate
methods in the adapter (ie the subclass) to adapt the inherited methods (figure 7). We’ll
implement this in example 10.3. It’s quite a neat solution, but in Delphi or Java, which do
not have private inheritance, it exposes the original methods. Without multiple inheritance,
it also does not allow a polymorphic adapter as in example 10.2.
Figure 7 TNewCounter as a
class adapter for TCounter
Because an adapter codes to a particular class signature and reduces coupling through
encapsulation, we should be able to take example 10.1 and change the implementation of the
adapter class without needing to change either of the other classes. We’ll see this now, in
this example, where the other two classes are not even aware that we have changed the
implementation of the TNewCounter class from an object adapter to a class adapter.
Instead of composing TNewCounter from TCounter as in example 10.1 step 2 (or step 4), we
can alternatively subclass TNewCounter from TCounter (line 7 below). This means that we
no longer need to propagate the Create and Destroy and so this class definition is quite a bit
shorter. Because Total is declared as protected in example 10.1, step 1, lines 5–6,
TNewCounter can access its value directly in the GetValue method (line 28 below). (If Total
had been declared as private, we would have to introduce an FValue data field for
TNewCounter as in example 10.1, step 4, line 9.)
We could add an Assign method here too, but because TCounter is not derived from
TPersistent we would not be able to take advantage of TPersistent’s Assign and so would
not be able to invoke the inherited Assign.
1 unit NewCounterU;
3 interface
4 uses
5 CounterU; // contains adapted class (the superclass)
6 type
14 implementation
15 uses
16 SysUtils; // for IntToStr
Chapter 10, Page 14 Object orientation with Delphi (all rights reserved)
17 { TNewCounter }
22 procedure TNewCounter.Clear;
23 begin
24 ClearAndRead;
25 end; // end procedure TNewCounter.Clear
When we run this version it works as before without needing to change either of the other
two units. A potential problem here is that through the inheritance TNewCounter exposes
all of TCounter’s public methods and data, and so this implementation provides weaker
encapsulation than the composition approach of the object adapter. As we’ll see in the next
step, there is a workaround if there are good reasons for using a class adapter instead of the
object adapter as in example 10.1.
As mentioned in chapter 7 while discussing inheritance anti-patterns, one can use a kludge
to hide the superclass’s methods and so prevent access to a superclass’s public methods
through the subclass. It is usually best avoided, but it is useful to know about it. The
approach is that for each method that must be hidden in the superclass one provides a
method in the subclass to hide the superclass method by raising an exception in the
subclass.
1 unit NewCounterU;
3 interface
4 uses
5 CounterU; // contains adapted class
6 type
17 implementation
18 uses
19 SysUtils; // for exceptions
20 { TNewCounter }
30 procedure TNewCounter.Clear;
31 begin
32 inherited ClearAndRead; // call inherited version
33 end; // end procedure TNewCounter.Clear
TCounter inherits two methods, AddAndRead and ClearAndRead from TCounter. To hide
these (in the interests of better encapsulation) we re-implement them in TNewCounter and
raise an exception should these ever be invoked (lines 28 & 37). The Adapter methods
introduced in TNewCounter must now call the inherited methods (lines 23 & 32) to avoid
rasing these exceptions themselves.
Chapter 10, Page 16 Object orientation with Delphi (all rights reserved)
Invalidating superclass methods as shown here is messy and error-prone, and so it is
generally better to use delegation and implement object adapters (eg examples 10.1 and
10.2) in preference to using inheritance and class adapters as in this example.
We mentioned earlier that one can use a class adapter to make a class available in a different
hierarchy where it will be used polymorphically. This requires multiple inheritance. It is not
possible in Delphi or Java, but is possible in C++. To illustrate why this needs multiple
inheritance, figure 8 shows the object adapter of figure 4 restructured for a class adapter.
TCupboard is no longer composed of TTheCupboard but is derived both from it and from
TFurniture.
We will leave the class adapter now and move on to look at a pattern statement for an object
adapter.
Therefore,
have ClassA, the Adapter, delegate the required operations to ClassB, the Adaptee. The
Adapter’s delegating methods operate polymorphically in hierarchy A while the Adaptee’s
delegate methods perform the required tasks. The Adapter’s and the Adaptee’s methods
may have different signatures. The Adapter’s clients are unaware of the delegation and the
Adaptee is unaware of the Adapter. The Adapter may have to perform some local
processing and store some local data in addition to delegating the operations.
This describes the Object Adapter, often referred to simply as the Adapter. A Class
Adapter is also possible (example 10.3), but is mainly justified in languages with multiple
inheritance, and so we do not present it as a pattern here.
An Adapter is sometimes called a Wrapper, but the term Wrapper is also used in other
ways. So for clarity we use the term Adapter when referring to this pattern.
At an implementation level, the Facade pattern is very similar to the Adapter. Conceptually,
it provides a simple interface with controlled visibility to a group of objects or to a complex
object. So where the purpose of an Adapter is to adapt an object in some way or other, the
purpose of a Facade is to reduce the coupling between a set of objects (or a single complex
object) and the rest of the system. We’ll explore this by developing a Facade for a RAD-
generated user interface object.
As we saw in chapter 8, when Delphi creates a RAD form, all the data (ie the
components) and the behaviour (ie the event handlers) have published visibility so that they
can be accessed through the Object Inspector. A form is a complex grouping of objects
similar to a composed object but without controlled visibility. In chapter 8 we provided
some encapsulation by making a particular object private (example 8.5 step 1 line 12) and
then writing an access method for just the property we were interested in. The Facade
pattern offers an alternative approach. It is simpler and more elegant in the sense that we do
not have to modify a RAD generated form to hide components. Instead, we create an
intermediate object, the Facade, which, through delegation, provides access only to the
required methods and data.
Chapter 10, Page 18 Object orientation with Delphi (all rights reserved)
Consider an application where we need to prompt the user for two string inputs. We
could make two consecutive calls to InputBox, getting each input separately, but we’d prefer
both inputs on the same Form (figure 9):
We’ll consider the ‘typical’ way to do this and the accompanying weaknesses as a way of
introducing the Facade.
Start a new application and add a second form. This will be the (RAD generated) input
Form (figures 9 & 10). Set the BitBtn bmbCancel’s Kind property to bkCancel and its
ModalResult property to mrCancel. Clicking this button then automatically closes the form
without the need for an OnClick event handler.
1 unit GetInfoU;
2 interface
3 uses
4 Windows, Messages, SysUtils, Variants, Classes, Graphics,
5 Controls, Forms, Dialogs, StdCtrls, Buttons;
6 type
7 TfrmGetInfo = class(TForm)
8 { Standard RAD generated preamble; ref figures 9 & 10 }
23 implementation
24 {$R *.dfm}
42 procedure TfrmGetInfo.SetDefaultText;
43 begin
44 edtString1.Text := ''; // set to required default string
45 edtString2.Text := ''; // set to required default string
46 end; // procedure TfrmGetInfo.SetDefaultText
If this form is closed for any reason except a click on the Update button, the values of the
Edits are set to the required default through SetDefaultText.
Chapter 10, Page 20 Object orientation with Delphi (all rights reserved)
Ex 10.4 step 2 Using the input form
We’ll test this using the application’s main form to create a simple driver (figure 10).
1 unit ShowInfoU;
2 interface
19 implementation
21 {$R *.dfm}
This version overwrites the name and phone number with blanks if the user cancels or
closes frmGetInfo. If we preferred that these labels are altered only when the user clicks on
btnUpdate, we could insert a conditional statement and replace lines 27–28 by:
This is a simple program and it works. But if we get fussy, and in programming it often pays
to be fussy, there is a problem with each of the program lines we have entered (lines 24–28)
because the chaining breaks the ‘neighbours-only’ rule of the Law of Demeter. The problem
is the degree of coupling between frmShowInfo and frmGetInfo (from step 1). To keep the
user interface object frmShowInfo general, and so to encourage reuse by other forms, we set
the Captions explicitly. But this means that we have to know the names of the relevant
objects in frmGetInfo (lines 24–25) in order to set up the access chain. This breaks
frmGetInfo’s encapsulation. If in future we change the Labels to GroupBoxes, or even if we
just change the names of the Labels, each one of frmGetInfo’s users will have to change
accordingly. There are similar problems with lines 27–28 where we also explicitly address
objects contained within frmGetInfo. Thus frmShowInfo is coupled with five other objects:
frmGetInfo, lblString1, lblString2, edtString1 & edtString2.
In line 26 we show the Form modally. This is important because if we call it non-modally
with Show rather than ShowModal, lines 27–28 will execute before the user has had time to
provide the input. This is a different way of breaking encapsulation: in frmShowInfo we
have to know that we must show frmGetInfo modally in order to use it.
There is a third problem too. Because frmGetInfo and its constituents are all
instantiations of Components, their properties and methods have Published visibility, which
is wider even than Public visibility. So frmShowInfo’s methods can change the size, colour,
position, Caption (etc, etc) of frmGetInfo and its constituent Components. Yet another,
serious, breakage of frmGetInfo’s encapsulation!
So what at first glance looks to be an innocent, everyday program turns out to be an OO
purist’s nightmare! (Of course, we’re over-dramatising the problems a bit here, but it serves
to illustrate the principles we are working with.)
Chapter 10, Page 22 Object orientation with Delphi (all rights reserved)
What can we do? Well, when trying to prevent encapsulation leakage, two thoughts
come up. First, can we use composition and then expose only what is needed? Second, can
we place an intervening class between frmShowInfo and frmGetInfo? We’ll combine both of
these in the Facade Pattern to reduce coupling and to increase encapsulation significantly.
Because of the problems we’ll recode example 10.4 and replace the explicit delegation with
an intermediate Facade object.
In the new version, the main form (TShowInfo) will communicate solely with the Facade.
This in turn will delegate all the operations to the TGetInfo form (figure 12).
1 unit FacadeU;
2 interface
3 uses
4 GetInfoU;
5 type
6 TMyFacade = class(TObject)
7 private
8 FIn2: string; // Keep these general
9 FIn1: string;
10 FInputForm: TfrmGetInfo;
11 FUpdateValid: boolean;
12 public
13 property In1: string read FIn1; // read only
14 property In2: string read FIn2; // read only
15 property UpdateValid: boolean read FUpdateValid; // read only
19 implementation
20 { TMyFacade }
21 constructor TMyFacade.Create;
22 begin
23 inherited;
24 FInputForm := frmGetInfo; // association: don’t create, just link
25 end; // end constructor TMyFacade.Create
TMyFacade now has all the links that were formerly part of frmShowInfo (example 10.4,
step 2, lines 24–28) packaged as a single method call (lines 28–32). The client need now only
interact with TMyFacade. Thus the coupling with the client is reduced significantly and the
complexity of the interaction with the input form is concentrated in TMyFacade.
TMyFacade interacts with a RAD generated form, TfrmGetInfo, which is instantiated
automatically when the program starts. Here we are therefore not busy with a composition
relationship between TMyFacade and TfrmGetInfo, but with an association relationship.
Consequently TMyFacade does not need to propagate its construction or destruction to
TfrmGetInfo. TMyFacade does, however, still need a constructor to create the association
between the two.
19 implementation
20 uses
21 FacadeU;
22 var
23 InData: TMyFacade;
Chapter 10, Page 24 Object orientation with Delphi (all rights reserved)
24 {$R *.dfm}
We change the uses clause to FacadeU (lines 20–21 above) and declare a TMyFacade
variable (line 23) which we create in the Form’s OnShow event handler (line 34). In the
btnGetInfo event handler we now interact with only one object, the instantiation of
TMyFacade (lines 28–30), rather than with five objects as previously (example 10.4, step 2,
lines 24–28). Access to the input form is now restricted to TMyFacade’s AcquireInput
method and to the two read-only properties In1 and In2.
If in the future our requirements change and we need to get the input from a database
query rather than through a user input form, we make all these changes to TMyFacade. Say,
for example, that instead of delegating the input collection to a Form, TMyFacade delegates
it to a database query, and that instead of using the AcquireInput method’s parameters for
Captions, it uses them to access database fields. These changes can all be accommodated
within TMyFacade while frmShowInput, and any other clients, remain blissfully unaware of
any changes. Without the Facade, clients such as frmShowInput would need significant
alteration to use the database. By allowing communication with only a single object (which
in turn delegates the operations as needed), a Facade reduces coupling between parts of a
system and so limits the impact of changes. Also, if any other form also needs the services of
the GetInfo form it too can work through this Facade and not delegate explicitly and
separately to each of the five objects as in example 10.4.
So, at the initial cost of some extra complexity, the improved encapsulation gives a much
more robust solution that is also more resilient in the face of future change and evolution.
Notice that the Facade exposes only those methods and data that the client needs. Here
the client sees only the AcquireInput method and the In1 and In2 properties and so can no
longer interfere with any of the other published properties of frmGetInfo (cf example 10.4,
step 2).
These benefits become far more obvious as overall system complexity rises and when the
Facade is used repeatedly and/or by several clients.
At times, a program may need to interact in a relatively limited way with several related
objects or with a single very complex object. It would be preferable to provide a simple view
of these objects or this object and so reduce the potential coupling and/or simplify the
coding.
Therefore,
create a Facade, which is an intermediate class with the simplified interface and which
delegates operations implicitly to the set of objects or to the complex object. Because of the
delegation, clients need not engage with the full complexity of the encapsulated object(s)
and the Facade can accommodate any changes needed in the encapsulated object(s) without
involving the clients.
The Facade pattern reduces complexity in several ways. It provides a single access class
to a group of objects with all access by clients to the group through itself, so reducing
coupling. It enhances ing encapsulation by exposing only the required data and behaviour
and keeping all other data and behaviour hidden. It provides a simple interface to a
complex object in cases where the full complexity is not needed.
Chapter 10 summary
Main points:
1. Using delegation (in the form of composition or association) in implementing patterns.
2. Using the Adapter pattern to:
a. encapsulate and adapt legacy objects or software, and
b. to extend polymorphism to classes outside a particular hierarchy.
3. Object and class Adapters.
4. Using the Facade pattern to:
a. decrease system coupling and to increase cohesion, and
b. to simplify interfacing.
5. Fabricating an intermediate class between other classes.
Chapter 10, Page 26 Object orientation with Delphi (all rights reserved)
Objects as derived entities: Applying delegation; creating an adapter through delegation (an
object adapter) and through inheritance (a class adapter).
Objects as interacting entities: Adapting an existing object to work with a class that expects a
different object signature; reducing coupling by an intermediate object that presents a
facade to an existing object or objects.
Pattern references
The Adapter and Facade patterns are both widely known and used, and are discussed by
Gamma et al (1995), Grand (1998), Lethbridge & Laganière (2001) and Shalloway & Trott
(2002), among others. Bennet et al (2002) apply the Facade pattern as part of a case study.
Bennet, S., McRobb, S. & Farmer, R. 2002. Object-oriented Systems Analysis and Design
using UML, 2nd ed, McGraw-Hill, Maidenhead.
Gamma, E., Helm, R., Johnson, R. and Vlissides, J. 1995. Design Patterns: Elements of reusable
object-oriented software. Addison-Wesley, Reading, MA.
Grand, M. 1998. Patterns in Java, vol 1. Wiley: New York.
Lethbridge, T. and Laganière, R. 2001. Object-oriented software engineering. McGraw-Hill:
Maidenhead.
Shalloway, A. and Trott, J. 2002. Design Patterns Explained: A new perspective on object-oriented
design. Addison-Wesley.
Problems
Identify the appropriate example(s) or section(s) of the chapter to illustrate each comment
made in the summary at the end of chapter 10.
Add Assign and AssignTo methods to the simple Adapter of example 10.1 step 2, and test
first Assign and then AssignTo with the driver program of example 10.1 step 5.
Some patterns seem to have the same, or very similar, implementations (eg UML diagrams
and code). However the patterns are used to match a particular problem in a particular
context and so the intentions of different patterns differ. As a novice OO programmer, one
tends to look at the implementation of the different patterns. However, as the
implementation aspects become more familiar, we start to see more clearly the match
between a particular problem and a particular pattern.
There are many similarities between the Adapter and the Facade. Discuss these and also
the differences between the two patterns, indicating when to use which pattern.
Example 11.5 works because the GetInfo form is displayed modally. As an exercise, rewrite
this to show GetInfo non-modally and draw matching sequence diagram(s).
Some background may help in understanding some of the reason for this question. In
much of our programming we use synchronous interactions between objects. We see this in
typical procedure or function calls where the calling routine waits for the called routine to
complete before continuing further. It is also possible to have asynchronous interactions
between objects. In asynchronous interactions an object (ObjectA, say) initiates some
operation (in ObjectB, say) and then continues without waiting for the operation to
complete. If ObjectA depends on the outcome of this operation in ObjectB we need some
mechanism so that ObjectA can use the outcome of the operation once it is complete.
ShowModal creates a synchronous relationship between two interface objects while
Show creates an asynchronous relationship.
Chapter 10, Page 28 Object orientation with Delphi (all rights reserved)
Problem 10.5 Catalogue entry for the Adapter pattern
Continue building the pattern catalogue by creating a catalogue entry for the Adapter
pattern.