Olaf Musch - Design Patterns With Java - An Introduction-Springer Vieweg (2023)
Olaf Musch - Design Patterns With Java - An Introduction-Springer Vieweg (2023)
Design
Patterns
with Java
An Introduction
Design Patterns with Java
Olaf Musch
© The Editor(s) (if applicable) and The Author(s), under exclusive licence to Springer Fachmedien Wiesbaden
GmbH, part of Springer Nature 2023
This work is subject to copyright. All rights are solely and exclusively licensed by the Publisher, whether the
whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations,
recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information
storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now
known or hereafter developed.
The use of general descriptive names, registered names, trademarks, service marks, etc. in this publication does
not imply, even in the absence of a specific statement, that such names are exempt from the relevant protective
laws and regulations and therefore free for general use.
The publisher, the authors, and the editors are safe to assume that the advice and information in this book are
believed to be true and accurate at the date of publication. Neither the publisher nor the authors or the editors give
a warranty, expressed or implied, with respect to the material contained herein or for any errors or omissions that
may have been made. The publisher remains neutral with regard to jurisdictional claims in published maps and
institutional affiliations.
With approved content from the 1st edition of “Florian Siebler-Guth, Design Patterns with Java”.
This Springer Vieweg imprint is published by the registered company Springer Fachmedien Wiesbaden GmbH,
part of Springer Nature.
The registered company address is: Abraham-Lincoln-Str. 46, 65189 Wiesbaden, Germany
A Warm Welcome to You!
v
vi A Warm Welcome to You!
programming (OOP). At the time of writing, I created the examples with NetBeans 12.6
and Java 16 on a PC with Windows 10, and there they work as described.
As for the English translation of this book, AI is terribly bad in translating names of
classes, methods, or variables within source code. So it took some amount of manual work
to adjust these and the comments in all the examples. If you happen to come across some
odd-sounding remains of German names and abbreviations, it’s purely my fault. If you
like, drop me a note on this. In the closing chapter, you’ll find my email contact.
To conclude this preface, I would like to thank the most important people who have
contributed directly and indirectly to the creation of this book:
Florian Siebler-Guth, whose book first gave me the idea to get back into Java program-
ming after a long time, and then also gave me the opportunity to incorporate my comments
into a follow-up edition myself.
Benjamin Sigg, who assisted me as a technical reviewer with his vast Java experience.
At Springer-Verlag Sybille Thelen, who actively supported us in the rededication of the
authorship, and David Imgrund, who then as an editor always advised me in a very open
and friendly manner.
To my wife Christine and our daughter Jessica, who had to (or were allowed to – a mat-
ter of perspective) put up with my reclusiveness during writing times.
vii
viii Contents
3.8 Summary�������������������������������������������������������������������������������������������������� 30
3.9 Description of Purpose���������������������������������������������������������������������������� 31
4 Template Method ������������������������������������������������������������������������������������������������ 33
4.1 How Template Method Works ���������������������������������������������������������������� 33
4.1.1 A First Approach ���������������������������������������������������������������������� 33
4.1.2 The Second Approach �������������������������������������������������������������� 34
4.1.3 The Hollywood Principle���������������������������������������������������������� 35
4.1.4 Introducing Hook Methods ������������������������������������������������������ 36
4.2 The “ListModel” Interface���������������������������������������������������������������������� 37
4.3 Template Method – The UML Diagram�������������������������������������������������� 38
4.4 Summary�������������������������������������������������������������������������������������������������� 38
4.5 Description of Purpose���������������������������������������������������������������������������� 39
5 Observer���������������������������������������������������������������������������������������������������������������� 41
5.1 Introduction���������������������������������������������������������������������������������������������� 41
5.2 A First Realization ���������������������������������������������������������������������������������� 41
5.3 Extending the Approach�������������������������������������������������������������������������� 43
5.4 Observer in the Java Class Library���������������������������������������������������������� 45
5.5 Concurrent Access ���������������������������������������������������������������������������������� 45
5.5.1 Synchronize Accesses �������������������������������������������������������������� 47
5.5.2 Copying the Database �������������������������������������������������������������� 48
5.5.3 Use of a Thread-Safe List �������������������������������������������������������� 49
5.6 Observer as Listener�������������������������������������������������������������������������������� 49
5.7 Listener in GUI Programming ���������������������������������������������������������������� 51
5.8 The Model-View-Controller Pattern�������������������������������������������������������� 56
5.9 Observer – The UML Diagram���������������������������������������������������������������� 57
5.10 Summary�������������������������������������������������������������������������������������������������� 57
5.11 Description of Purpose���������������������������������������������������������������������������� 59
6 Chain of Responsibility��������������������������������������������������������������������������������������� 61
6.1 A Real World Example���������������������������������������������������������������������������� 61
6.2 First Code Example: Grocery Shopping�������������������������������������������������� 61
6.2.1 The Foodstuffs Required���������������������������������������������������������� 62
6.2.2 The Sellers�������������������������������������������������������������������������������� 62
6.2.3 The Client���������������������������������������������������������������������������������� 64
6.3 An Example from the Class Library�������������������������������������������������������� 66
6.4 Chain of Responsibility – The UML Diagram���������������������������������������� 68
6.5 Summary�������������������������������������������������������������������������������������������������� 69
6.6 Description of Purpose���������������������������������������������������������������������������� 69
7 Mediator���������������������������������������������������������������������������������������������������������������� 71
7.1 Distinction from the Observer Pattern ���������������������������������������������������� 71
7.2 Task of the Mediator Pattern�������������������������������������������������������������������� 72
Contents ix
In this chapter I give an overview of what design patterns are. I show you the historical
background, that is, where patterns find their origin. I also draw your attention to the advan-
tages and disadvantages of patterns. Furthermore, you will learn how to categorize patterns.
And finally, I introduce the template by which patterns are described in Gamma et al.
In this section, I describe what design patterns are and how you can benefit from them.
• Patterns are described by a set of information. The context says something about when
the pattern applies: You want to have a window in a wall. The problem names a conflict
of goals: the window sill must not be too high and not too low. The solution shows at
an abstract level how the problem has been solved successfully in the past: the window
sill is at a height between 12 and 20 inches.
• A pattern is not a dogma that must necessarily be followed. Rather, there may be rea-
sons to deliberately violate the 12–20 inch rule. For example, only skylights are often
installed in certain spaces: prisons or swimming pool shower rooms. Conversely, deco-
rated storefronts are not in danger of being mistaken for doors; thus, their sills may be
lower than 12 inches.
• Patterns do not give specific recommendations for action. In the example, Alexander
suggests planning the window sill at 12–14 inches or at about 20 inches. Thus, when
designing a building plan, an architect finds a wide range to consider depending on the
context. Consequently, patterns are not algorithms or concrete implementations, but
abstract proposed solutions that must be adapted to the problem at hand.
Ten years later, in 1987, Kent Beck and Ward Cunningham develop five patterns for GUI
development in a similar fashion and present them at OOPSLA 1987 (http://c2.com/doc/
oopsla87.html). Important to note: The characteristics of patterns just mentioned apply
to software development patterns in the same way.
In 1994 Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides formulated a
total of 23 patterns for software development in their book “Design Patterns – Elements of
Reusable Object-Oriented Software”. In this book the hype about patterns finds its begin-
ning. In addition to the pattern catalog by Gamma et al. there are meanwhile numerous
other pattern catalogs for different programming languages and different application areas.
Gamma et al. are aware of this. They write that their collection is not complete and fixed,
rather it is a snapshot of their thoughts. In their view, the collection contains only part of
what experts might know; in particular, it lacks patterns for concurrency, distributed pro-
gramming, or real-time programming.
To avoid a Babylonian confusion of language, please note: When the following text
refers to “design patterns”, “patterns”, “patterns” or “design patterns”, it means
design patterns of software development.
Patterns, as I just wrote, do not describe concrete solutions, algorithms, and certainly not
prêt-à-porter implementations that you could simply copy-and-paste into your applica-
tions. Why should you bother with patterns anyway? What benefit do you get from
1.1 What Are Design Patterns? 3
working your way through the catalog of patterns described in this book? I’d like to pro-
vide the answer in this section.
Before you start implementing a pattern, let’s discuss the question of what a pattern is in
the first place. If you search the Internet for singleton, for example, you will find an imple-
mentation for every programming language. Even for Java, there is an implementation that
1.1 What Are Design Patterns? 5
is perhaps something of a “standard”. But this implementation is not the Singleton Pattern
itself. A pattern itself only describes what the goal is, what to look for when implementing
the pattern, and what the advantages and disadvantages are. GoF presents a code example
for each pattern in its book. Nevertheless, it is up to you alone how to realize the pattern.
This is the only way to transfer patterns to all languages with their respective peculiarities.
Compare this with the construction plan of a house, which describes where the walls are,
but not how the walls are to be built.
Most books on design patterns show concrete implementations that have proven practi-
cal; I also present these “standards” to you. Nevertheless, in the spirit of the pattern, it is
allowed to find a different solution.
So what are design patterns in software development? Design patterns describe solutions
for recurring problems on an abstract level. They bring together the knowledge of the
developers. This makes it easier to pass on experience and to fall back on proven solutions,
i.e. to reuse them. In addition, they expand the vocabulary of the developers.
Among other things, you will get to know the State Pattern and the Strategy Pattern. In
a direct comparison of these two patterns, you will see that a pattern always moves in a
certain context, or always solves a clearly defined problem.
Once you have worked through this book, how do you put the patterns into practice? When
you have read this book, you will not have every pattern with all its details in your head.
You should not develop this ambition at all. But you have heard that there is a pattern that
describes what to do in situations when an event source is supposed to inform its observers
about state changes. You pick up my book in the concrete situation of your everyday life
and read the structure, the advantages and disadvantages again. Does the pattern fit your
specific situation? Is the application of the pattern appropriate? Based on these questions
you will solve your concrete problem. The more patterns you know and have applied in the
past, the more your wealth of experience will increase and the more flexibly and unerr-
ingly you will be able to use and evaluate patterns. Programmers are like wine – the older
they get, the better they become.
6 1 The Term “Design Pattern”
Tip
But please use patterns wisely. Knowledge of design patterns should not tempt you
to lard your software with patterns. Most patterns require you to develop additional
classes. If you try to implement as many patterns as possible in a manageable appli-
cation, the result will be a poorly maintainable, unnecessarily bloated system. Have
the courage to forgo a pattern or even remove a pattern! Patterns are not an end in
themselves. Your goal as a programmer should be to create clear and unspectacular
program code.
Let me mention one more point in this context: Extensions. A constant of applica-
tion development is that requirements – and thus the source code – grow. On the one
hand, it makes sense to design programs in such a way that they can be extended in
the future. But on the other hand, targeting future requirements should be done very
cautiously. Keep in mind that you are writing programs to be used in the “here
and now”.
On the website stackoverflow.com, a large developer community that has been
around since 2008, I found the following phrase that I really liked: “Design Pattern
is meant to be a solution to a problem, not a solution looking for a problem.”1
Design patterns are grouped into categories and described according to a specific template.
I show these formalities in this section.
The Gamma et al. pattern catalogue contains 23 patterns. These are divided into three
categories. Within these categories, a further distinction is made as to whether a pattern is
more object-based or more class-based. Here I present the three categories:
Creational Patterns: They hide the creation process and help make a system independent of
how its objects are specifically created, composed, and represented. A class-based creational
pattern uses inheritance to vary the class of the object being created, while an object-based
creational pattern delegates creation to another object. There are two recurring leitmotifs in
these patterns. First, they all encapsulate knowledge of the concrete classes used by the sys-
tem. Second, they hide how instances of these classes are created and assembled. Everything
the application knows about the objects is determined by the defined interfaces.
1
https://stackoverflow.com/questions/11079605/adapter-any-real-example-of-adapter-pattern#com
ment14506747_11079605
1.2 Categorize and Describe Design Patterns 7
Structural Patterns: They deal with the composition of classes and objects to form larger
structures. Class-based structural patterns use inheritance to merge interfaces and imple-
mentations. Object-based structural patterns, on the other hand, describe ways to merge
objects to gain new functionality.
Behavioral Patterns: they deal with algorithms and the assignment of responsibilities to
objects. They describe not only patterns of objects or classes, but also the patterns of inter-
action between them. Class-based behavior patterns use inheritance to distribute behavior
among classes. Object-based behavior patterns use composition instead of inheritance.
Patterns in this category describe complex flows of control that are difficult to trace at run-
time. This shifts the focus away from control flow to how objects interact with each other.
Table 1.1 shows an overview of the categories and the patterns. It is noticeable that there
are more object-based patterns than class-based ones. The “Adapter” pattern is both class-
based and object-based. You may not be familiar with all of the patterns. That doesn’t
matter-the purpose of this book is to introduce you to all the patterns in detail.
Documentation is often structured according to the same template – think, for example, of
the arc42 documentation template for software architectures, which you can find at www.
arc42.com. If the reader knows the template, he immediately knows where to find which
information. Patterns are also documented according to a uniform scheme. From a bird’s
eye view, each pattern must have at least one name, as well as one section each about the
8 1 The Term “Design Pattern”
Table 1.2 Categories of design patterns according to Buschmann et al., pages 376 f
Architectural
Patterns Design Pattern Idiom
From chaos to structure Layers Interpreter
Pipes-and-filters
Generation Abstract factory Singleton
Prototype Factory method
Builder
Structural Composite
decomposition
Organization of work Master-slave
Chain of responsibility
Command
Mediator
Access control Proxy
Facade
Iterator
Variation of services Bridge Template
Strategy method
State
Service extension Decorator
Visitor
Management Memento
Adaptation Adapter
Communication Publisher subscriber
(observer)
Resource management Flyweight
problem to be solved, the concrete solution itself, and about possible consequences when
applying the pattern.
In order to describe this minimum set of information in a uniform way, Gamma et al.
use the following template:
Pattern name: The name of the pattern is a keyword that uses one or two words to
name the problem, solution, and implications. The name is part of the vocabulary and
allows us to communicate and document at a higher level of abstraction.
Category: The category is based on the classification described in Sect. 1.2.1.
Purpose: This section briefly describes the purpose and aim of the design.
Also known as: Where there are other names for the design, they are referred to in this section.
Motivation: A concrete scenario is used to demonstrate a problem and explain how the
pattern solves this problem. The scenario promotes the understanding of the abstract rela-
tionships behind the pattern.
Applicability: This section describes in which situations the pattern can be applied. So
here the context is mentioned.
1.2 Categorize and Describe Design Patterns 9
Structure: The classes and objects involved in the pattern are represented in diagrams.
Gamma et al. use OMT, a precursor of UML, for this purpose.
Participants: The classes and objects involved are described. You will also learn there
which tasks they have.
Interactions: In this section, you will read how the participants work together.
Consequences: Each pattern has advantages and disadvantages; these are mentioned
here. You will get hints for implementation, especially traps and helpful tips.
Implementation: code snippets demonstrate the realization of the pattern as well as
different implementation variants.
Known uses: You will learn where the pattern is used. This is useful to get examples of
the use of a pattern.
Related patterns: Often patterns look very similar; you will learn where the differ-
ences are, but also how two patterns work together.
I will not list these sections in the book as formally as Gamma et al. It is important to
me to present the relevant information to you in a way that is easy to understand – and to
do this I am deliberately breaking with the formal structure. However, just like Gamma
et al., I will describe one pattern in each chapter. At a second point, I deviate from Gamma
et al.’s scheme: I cite the purpose description only at the end of a chapter. The reason for
this is that I myself have had the experience that when I read the purpose description, I do
not initially understand what is meant by it. On the other hand, if I have dealt with the pat-
tern beforehand, it is catchy. In short, if I understand the purpose description, I understand
the chapter as a whole. And that’s exactly the kind of learning check I want to give you.
In the next chapter I will introduce object-oriented design principles. After that,
starting in Chap. 3, I will describe the patterns. The examples use the current (at the
time of writing this book) language features of Java 16 – including some preview
features. As far as these help to understand the meaning of a pattern or to keep the
source code simple, I go into more detail. At the very least, I then mention the names
of APIs or numbers of pertinent “specification requests” that you can use as refer-
ence points for your own research on these features. However, I essentially assume
that you know the language features of Java 16 at least roughly. The goal of this
book is to explain the patterns. Sometimes the newer language features of Java help
with this, but sometimes they make things more “complicated” or the code longer
than necessary. In this case, I don’t use them. In the book I print only the most neces-
sary aspects of the examples. In this form the programs would not be executable, and
I did not mark the cuts everywhere. I have also removed source code comments here
for printing, but they are there. The link to the additional material of a chapter can
be found at the beginning of each chapter in the footer.
10 1 The Term “Design Pattern”
You will find in the zip file you download the source codes as NetBeans projects,
which you can open directly in NetBeans. The development environment I person-
ally prefer, NetBeans itself, can be found at https://netbeans.apache.org/. Of course
you can use any other development environment you like. Even with a simple text
editor and a command line you can develop Java applications.
If I mention an example project in a chapter, you will also find it in the subfolder of
the respective chapter. The same applies to special documents or files that are rele-
vant in the respective context.
1.3 Summary
• Design patterns are proven solutions to an indefinite number of problems that recur in
this or a similar form.
• Design patterns create a vocabulary that is part of general education for programmers.
• Design patterns cannot be invented, only discovered – just like the number Pi.
• The GoF – Gang of Four – has been instrumental in introducing design patterns into
software development.
• The GoF book includes 23 patterns, but there are many more.
• The GoF has divided its patterns into three categories:
–– Creation patterns describe how objects can be created efficiently,
–– Structural patterns describe how classes can be combined to form meaningful
larger units,
–– Behavior patterns describe the interaction of individual objects.
1 The Term “Design Pattern” 11
Good software systems are not only implemented correctly, but are also characterized by
being extensible and understandable, among other things. In this chapter, I describe object-
oriented programming and some object-oriented design principles that promote correct-
ness, extensibility, and understandability.
You hear very often that you don’t need patterns if you master and apply the rules of object-
oriented programming. Is that true? How are patterns and object-oriented programming related?
If you program in an object-oriented way, you will certainly work with inheritance – you
have something general and derive something specific from it. In most Java introductions,
inheritance is presented as the ultimate. Yet inheritance has drawbacks, and I’d like to address
those now. You have a class and you create a subclass that inherits the behavior defined in the
superclass. You can now inherit further, but the further down the inheritance hierarchy a class
is, the lower the possibility of reusability. Also, any changes you make to the superclass will
affect all of the subclasses; this weakens the encapsulation, which usually indicates an inap-
propriate design. But probably the biggest disadvantage of inheritance is that the inheritance
hierarchy grows very rapidly in both breadth and depth. An example will illustrate this.
Let’s think about a software that is needed to manage the animals of a zoo. At the top
of the inheritance hierarchy of animals is certainly the class LivingBeing. In this class,
you want to store the number of legs. Wait – what if you want to include fish in the hierar-
chy? They’d have to carry the numberLegs attribute as well, even though there are no
fish with legs. Fine, let’s agree on the compromise that fish are not included in the inheri-
tance hierarchy. So, the superclass stores the number of legs. Let’s start with birds – birds
can fly, so you need to develop a subclass Bird that stores statements about flight behav-
ior, such as how fast a bird can fly. It occurs to me that penguins are also counted as birds,
even though they can’t fly. Now you have the choice of not including penguins in the
inheritance hierarchy either, or of creating two different subclasses: FlyingBird and
NonFlyingBird. But that should be the least of your problems for now. An animal has
legs to be able to walk. In the superclass LivingBeing, you now include the informa-
tion about walking behavior. This enables you to represent not only birds, but also dogs,
cats, camels, monkeys and lions. But wait – some birds can’t run at all, only hop. Again,
you have to worry about constraints in the inheritance hierarchy. Do you want subclasses
to inherit attributes and methods they don’t need? Or do you want to exclude hopping birds
from the inheritance hierarchy? If not, you must consequently create the following sub-
classes from the subclass Bird in addition to the subclasses FlyingBird and
NonFlyingBird: RunningBird and HoppingBird. However, most birds can both
run and fly. How could I deal with this situation? Actually, you would need to develop
classes that allow both running (alternatively hopping) and flying. So, you need a class
FlyingRunningBird, a class HoppingFlyingBird, etc.
To take things up a notch, you could start looking at the feeding habits of animals. A
lion is certainly a carnivore, and since it’s outside the complicated inheritance hierarchy of
birds, the requirement doesn’t sound particularly difficult. So, include the attribute quan-
tityMeatPerDay in the superclass LivingBeing - this information is certainly
important for the zoo! But wait. Since there are also pigeons in the inheritance hierarchy
of birds, they inherit the attribute as well. However, pigeons are not suspected of feeding
on meat, so they really shouldn’t carry this attribute around. So, the information about how
much meat per day an animal eats should not be stored in the class living beings. What is
the solution? Introduce a class CarnivorousNonBird below the class LivingBeing.
The lion can be an instance of this class. Cows, like pigeons, do not eat meat. So below the
class Living beings, develop the class VegetarianNonBird. This class will certainly
hold the information of how much meatless food the zoo needs to provide per day. You
already notice that another problem is beginning to mature. Among the birds there are also
carnivores like eagles and vegetarians like the pigeons. So you can start again on the
inheritance line of birds and distinguish between carnivores and vegetarians. Eagles will
then be instances of the class CarnivorousFlyingRunningBird.
Figure 2.1 shows how the class diagram for such a project could look like.
The example is certainly correct from the point of view of object orientation; neverthe-
less, the solution is not particularly practical. It contains too many compromises or restric-
tions and is also too inflexible. Design patterns suggest alternatives that help you create
flexible and extensible software using the methods of object-oriented programming.
I have deliberately exaggerated the example because I want to convince you that
inheritance can lead you into a very rigid system in which there is a hierarchy that
cannot be surveyed in breadth or depth. The classes are so special that they cannot
be reused in any other place.
2.1 Object-Oriented Programming 15
The example would certainly have been much better developed with composition.
Specifically, this means that you develop an interface FeedingHabit, from which the
classes Carnivore and Vegetarian are derived. These classes store how much meat
or how much grass is intended per day. The cow, just like the pigeon, must have the attri-
bute feedinghabit = new Vegetarian(). And consequently, both the lion and
the eagle will get the attribute feedinghabit = new Carnivore(). So, the rel-
evant behavior is not inherited, but defined in a separate class. The class diagram Fig. 2.2
shows the interaction.
What is gained by this? Your inheritance hierarchy becomes much clearer. In addition,
you can define new behavior very easily. You can subsume everything that makes good
software under the term “easy”: For example, places where changes need to be made can
be identified quickly; moreover, existing code does not need to be tested again. A change
is not only easier to maintain, but also less prone to errors. The following extension
16 2 Object-Oriented Programming and Design Principles
illustrates this statement. If you define snails and worms as meat, ducks must consequently
be carnivores; but they also eat grass, so you have a hybrid here. This is not a problem –
you let the Omnivore class inherit from the interface FeedingHabit, and you can
provide a new behavior without breaking existing code. The duck gets the attribute feed-
inghabit = new Omnivore(). In a corresponding way, the running habit could be
represented.
In the chapter on the Observer Pattern, I will point out the need to program against inter-
faces. What is gained by this? What exactly is meant by interface? The question about the
term interface is necessary, because I think it is important to distinguish it clearly from the
term interface.
Object orientation requires that you encapsulate the attributes of an object, the state of
an object. Access from the outside should not be possible. The attributes may only be
accessed indirectly via methods. Each method has an identifier, a parameter list and a
return value – this is the signature of a method. All public methods together form the inter-
face of an object. The type is the name for this interface.
Methods do not necessarily have to be defined; it is sufficient that they only declare the
signature; they are then abstract. It is the task of derived classes to override these methods
and implement them concretely. Derived classes are the subtype of the abstract class,
which is the supertype in an inheritance hierarchy. Classes with abstract methods are
themselves abstract. However, abstract classes can define non-abstract methods as well as
abstract methods. They can even define all methods, so that an abstract class declares no
abstract methods at all. If a class declares only abstract methods, you as a Java program-
mer can create it as an interface. Since Java does not allow direct multiple inheritance, the
introduction of interfaces is an important language element. You can implement as many
interfaces as you like, so that one object can correspond to – or “take on” – many inter-
faces. Even though we Java programmers usually mean interfaces when we talk about
interfaces for this reason, in the Patterns environment you must always keep in mind that
interfaces can be either interfaces or abstract classes.
2.2 Programming Against Interfaces 17
Background Information
Since Java 8, static and non-static methods can be defined in interfaces. It is there-
fore possible to “inherit” behavior from an interface. The difference between abstract
class and interface is becoming blurrier – against this background, it is also impor-
tant to interpret the term interface broadly in the context of design patterns.
The difference between abstract classes and interfaces is that interfaces define
roles, while abstract classes have the task of establishing an inheritance hierarchy.
Interfaces, unlike abstract classes, are stateless, and when you define a method in an
interface, you give the implementing class a default behavior. This is useful if you
want to extend an interface, but do not want to or cannot adapt all the implementing
classes.
It is always very laborious in programming when an interface is extended; all
classes implementing this interface have to be adapted. With the default methods it
has become much easier.
In the Observer Pattern and also in the Mediator Pattern, you will have any number of
objects that are related to each other. Look at the wine trading simulation there. You can
exchange the consumers there just as you can the producers. All that matters to the media-
tor there is that he is dealing with objects of type ConsumerIF or ProducerIF. It does
not matter which concrete class is behind it, whether it is a private customer or a corner
shop. The objects are loosely coupled, and loose coupling, like high cohesion, is a sign of
good design.
In the Observer pattern, you will put both students and employees in the “Observer”
role. For the Event Sources, it will only be important that they communicate with objects
that match the expected JobObserver interface and define the update() method.
Both the number and the type of specific implementation of the participants may vary in
both projects. Can I convince you that by programming against an interface, your imple-
mentation will gain substantially in independence and flexibility?
If not, I would like to refer to the chapter on the iterator as a final example. You will deal
with lists there. There are a variety of lists: ArrayList, LinkedList, and many more
from the class library, as well as, of course, the lists you program yourself. All of these lists
implement the List interface. Now, when a method returns a list – generally speaking, an
object of type List – you don’t have to worry about the implementation behind it. The
getList() method in the example below returns some list whose class you don’t know.
The testList() method calls the getList() method and has a list returned to it.
Then the size() method is called on the returned object, which is prescribed in the
List interface. At no point within the test method is it relevant whether you are returning
the size of an ArrayList, a LinkedList, or some other list.
18 2 Object-Oriented Programming and Design Principles
List list;
void testList() {
list = getList();
System.out.println(“Number of elements:” + list.size()
);
}
List getList() {
// ... returns any object of the type “List”
}
You develop good code if you stick to the principle that the GoF has formulated:
“Program towards an interface, not an implementation”.
would have been impossible to implement without a lot of effort if you didn’t violate the
principle. Also, regardless of the discussion about patterns, it is hardly feasible in practice
to really assign a single responsibility to a class. If you know you’re violating the principle
and have good reason to do so, feel free to violate it. Strive to achieve Single Responsibility,
but have in mind that the principle is not an end in itself. It is meant to help you increase
cohesion and loosen coupling. But it should in no way force you to choose a cumber-
some design.
You might want to skim this section when reading the book for the first time. I mention
many patterns here that you will only get to know in the following chapters. But be sure to
look here again at the end.
Code usually has components that never or rarely change. However, most of the time
there are also components that change or can change. Identify the variable code parts
and encapsulate them. Look at the Strategy Pattern. You will have a variety of different
strategies to choose from there to sort an array. In the very first version, you’ll use a variety
of if statements or a switch statement to determine the appropriate strategy for the specific
situation. This approach is unsatisfactory for several reasons: you’re lugging around code
in a class that, in doubt, you’ll never need. If you want to implement a new strategy, you
have to change existing code that has already been tested. If a strategy needs to be changed,
that change would affect all the code. Bugs are guaranteed to creep in if you have to
change code in multiple places.
With the State Pattern you get the same problem. As a consequence, you define variable
code parts in your own classes in both cases. Also consider the Template Method Pattern.
Here you have to define parts of the algorithm in subclasses. Or think of the Command
Pattern – you encapsulate commands in their own Command classes. Want more exam-
ples? Take a look at the Iterator. You can’t write a “universal iterator” because you’ll never
know all objects of type List. So, you put the responsibility where it belongs, in the appro-
priate subclass. Put another way, you encapsulate access to an aggregate. What happens.
You identify variable pieces of code and encapsulate them. In most cases, Strategy and
State for example, you define the code parts in question in new classes. In another case –
the Template Method pattern – you move the variable code part to a subclass.
In general, it makes sense to use inheritance very cautiously. In the introduction, I used
the example of zoo software to show you that inheritance can lead you into an unmanage-
able system under certain circumstances. As an alternative, I suggested composition. The
approach is exactly the same as what you will see with the Strategy Pattern and the State
Pattern. And in fact, most patterns resort to composition instead of inheritance. The GoF
has established the principle: “Prefer object composition to class inheritance.” Their rea-
soning is that “your classes and your class hierarchies [stay] small and [...] are less likely
to [grow] into uncontrollable monsters.”
20 2 Object-Oriented Programming and Design Principles
The open-closed principle goes back to Bertrand Meyer (Meyer, Bertrand (2009): Object-
oriented software construction 2nd ed., 15th ed. Upper Saddle River, NJ: Prentice Hall
PTR. ISBN 978-0136291558.), who requires that modules be both open and closed. Open
they must be in order to extend functionality. Closed they must be in the sense that code
may not be changed to implement new functionality. Meyer described this principle almost
30 years ago and developed several approaches to it. In his book, he sketches out some
possible solutions, discards them, and finally offers inheritance as a solution. From today’s
perspective, however, inheritance will never be considered the solution par excellence.
Today, when you develop systems that need to be both open and closed, you resort to
composition and design patterns: you program against interfaces and prefer composition
to inheritance. Above all, you deal with design patterns because their most sacred task is
to show you ways to make your systems conform to the OCP.
Let me give you two examples of how patterns help you develop systems that are both
open and closed. In the Template Method Pattern, you define an algorithm that is so sensitive
that it can’t be changed. Your product manager would get a stomachache if you tried to touch
the code again. The class may even have already been tested and shipped to customers. In
short, never ever, under any circumstances, will you want to change the code again. It is
closed to change. On the other hand, you need to allow your client to redefine parts of the
algorithm. To extend the existing system, he will write a class that inherits from your class.
However, inheritance is only one way to keep systems “open and closed”. With the
Strategy Pattern, you create the system so that each strategy is encapsulated in a class. If
the system needs to be extended with a new sorting algorithm, it is sufficient to define
another class that executes the desired algorithm. The user can use the algorithm without
changing existing code.
2.6 Principle of the Right Sense of Proportion 21
In the first chapter, I already recommended using patterns with caution, because they
almost always require you to define additional classes or interfaces. But it should be the
goal of programmers to produce unspectacular, uncluttered code. You will regularly have
to decide whether or not to use a pattern. You will need to rely on your experience when
making this decision. It is always a good idea to look critically at your own code and the
code of other programmers and think about it. The use of patterns is usually motivated by
the fact that you want to account for future changes. Change is the constant of software
development. That your code will grow is virtually a law of nature. Therefore, in the litera-
ture you will often find the advice that you should always build your systems with patterns
and keep them open to all sides in order to be prepared for any eventualities that may arise
in the future. I don’t support this approach in such absolutes. If I want to prepare my soft-
ware for all changes, the first release is certainly far from relying on easily maintainable
unspectacular code. Write good programs that can be used here and now. This is also, in
the vast majority of professional situations, exactly what a client/customer will be willing
to pay for. Therefore, produce lean code that can be quickly reviewed and changed when
needed. The KISS principle applies here: Keep It Simple, Stupid, roughly: As simple as
possible, as complex as necessary.
Agile software development does not contradict the use of design patterns – where it
makes sense. The problem on the other hand is that with too simple code you might get a
higher rework effort in case of later changes. With today’s possibilities of refactoring
(rebuilding), which practically every modern development environment usually supports
well, this is usually kept within limits or is at least “manageable” to a large extent. But then
let such tools help you. Use modern development environments. With dozens of classes, a
simple text editor quickly reaches its limits, even if it is technically possible and legitimate
to use it.
But enough of the preface, in the following chapters we will deal with the individual
Design Patterns, and at the end I will show you in a final step how you can combine several
patterns, for example.
Tip
Try it:
You could, if you want to invest a little time, review the patterns in the following
chapters to see if you can find the design principles again.
Do some research on the internet for more design principles – there are many
more than the ones I’ve presented. Have a look at the page.
http://www.clean-code-developer.de.
In the Facade Pattern chapter, you will learn about another design principle.
Singleton
3
If you’ve worked with patterns in the past, you’re probably familiar with the Singleton
Pattern; it’s probably the most well-known pattern. Therefore, we will simply start with
this pattern in order to find an “easy” introduction.
Imagine that you have a class that defines something that is so unique that there can only
be one instance of it. This one instance should be accessible via a global access point. Take
the metaclass of a class as an example. For each class that is loaded into the Java Virtual
Machine (JVM), an instance of a class is created that stores descriptive meta-information.
This object is so unique that it must exist only once in the JVM. You can query it with
String.class, but also with (new String()). getClass(). You can prove
that the return value is one and the same object by comparing the references. Enter the
following line in a Java shell:
System.out.println(String.class ==
(new String(). getClass()));
On the console, true is output, so the references are the same and thus object equality
is proven. This is in line with your expectations – it would be bad if there were two meta
classes of one class.
The singleton pattern comes into play in another place: Your application’s runtime envi-
ronment is so unique that it must exist only once. Consider the API documentation of the
Runtime class:
Every Java application has a single instance of class Runtime that allows the application to
interface with the environment in which the application is running. The current runtime can
be obtained from the getRuntime method. An application cannot create its own instance of
this class.
You will resort to a singleton yourself if you manage your application’s user rights, for
example, or perhaps program a cache. A typical application can also be a thread pool.
Also, resources like icons or images that you use in your program only need to be loaded
once and can be stored in a singleton. Let’s look at a few examples of how you can imple-
ment the Singleton Pattern.
So how can the singleton pattern be implemented? You should first ensure that there is
only one copy of the class. In Java, to prevent users from creating instances of a class, you
must first set the constructor to private.
Now, access to the constructor is only possible from within the class or through other
instances of this class. Since there is no (other) instance of the class, however, you must
ensure that the one instance is created within the class. So define a method that creates an
instance if there is no instance already; the instance of the class is returned to the caller of
the method. This method must be public, of course. Keep a reference to this instance in a
static variable:
That’s it! This is a simple way to implement the singleton pattern! The instance of the
class is created at the latest possible time, namely when it is needed for the first time.
Therefore, this procedure is called lazy instantiation, i.e. delayed loading.
You can find the code for this in the sample project Singleton_1.
In the application, you then get the reference to the one instance with MySingleton
single = MySingleton.getInstance() and then call the actual function of the
singleton with single.doSomething():
And here I use a feature added in Java 10 for local(!) variables: The so called “Local-
Variable Type Inference”.
Instead of.
Since version 10 with the JDK Enhancement Proposal (JEP) 286, Java is able to deter-
mine the type for local variables independently and without further specification by the
programmer out of the context. This happens at translation time, so it does not compro-
mise Java’s type safety. In the example here, it is already obvious to the reader from the
context that we want to have an instance of a MySingleton. The additional prefix
MySingleton has thus become superfluous. It is sufficient to use a.
BufferedInputStream inputStream =
new BufferedInputStream(...);
I will use this feature frequently in the sample code below, without going into further
detail. It is quite catchy and only allowed if the type of the local variable can really be
determined unambiguously. Therefore it should not cause you any problems in the fur-
ther course.
The class described above runs beautifully as long as you don’t use concurrency. What can
happen? Assume the following case: You have two threads, both accessing the getIn-
stance() method. The first thread gets to line 9 and finds that there is no instance yet.
Then the Virtual Machine takes away its compute time and lets Thread 2 access the method.
Thread 2 is allowed to work until the end of the method and creates an instance of the
class, which is returned. Then Thread 1 gets allocated compute time again. The last thing
he remembers is that the reference is zero. It will enter the block and create an instance as
well. And now comes exactly what you wanted to avoid – you have two instances of
the class.
Whenever you want to prevent code from being executed by two threads at the same
time, lock the affected code:
This code can be found in the sample project Singleton_2. The method can only be
entered by a thread when no other thread is working with it (anymore). This solves the
problem. However, synchronization is costly, so this approach is an unnecessary brake.
Imagine – every time you want to get the one instance of the class, a lock is set on the
method. So there must be an alternative.
Do not lock the entire method, but only the critical part! First query whether an instance
has already been created. If the method is called for the second time, the result of the com-
parison is false. Therefore, a lock is only required on the first call, when the comparison
instance == null returns a true. So inside the if statement, you create a block that
3.4 Double Checked Locking 27
is synchronized. In this block, you query – this time in a thread-safe manner – whether
there is an instance of the class. If not, create one. Finally, return the instance. Take a look
at the example project Singleton_3.
// … abridged
By the way, in NetBeans you get a hint for line 6 (“synchronized (MySingleton.
class)”). NetBeans detects double checked locking and still suggests the use of a local
variable to improve performance. If you’re using NetBeans, take a look at that too. It
makes the code a bit longer, so I’ll leave it out of the example here. This approach to
Double Checked Locking is correct in theory. However, in some circumstances you run
into a problem. Imagine the following scenario: You have two threads; thread 1 calls the
getInstance() method. At line 5, it detects that no instance of the class has been cre-
ated yet, and gets the lock on the following block. In line 7, it checks again – in a thread-
safe manner – whether an instance of the singleton class has really not yet been created. If
not, it creates an instance with the new operator (line 8). Depending on the runtime
environment, the following happens during instantiation: First, memory is requested, then
the memory is passed to the variable instance, which is now nonzero. Only in the next
step is the constructor of the class called. And now our problem begins to mature: thread
2 enters the getInstance() method before the constructor call by thread 1. It deter-
mines that instance! = null, gets the instance returned, and works with it. Only later
does Thread 1 execute the constructor, which puts the instance into a valid state.
You can prevent this by declaring the variable instance as volatile.
The problems around double-checked locking should almost justify a whole disserta-
tion. I would just like to point out here that this solution can lead to runtime errors. There
is no guarantee that the volatile keyword is implemented correctly in every virtual machine.
For example, a JVM in a version below 1.5 may not implement this approach correctly at
all. In such cases, you should strongly consider switching to more recent JVM versions.
However, I hope that in 2021, when we are at Java 16, I won’t have to convince anyone to
migrate at least to Java 8 (2014), better 11 (2018). Java 11 is the most current “Long Term
Support” version until Java 17 comes out (scheduled for fall 2021).
To avoid all problems in concurrent systems, you can resort to early loading. As usual, you
create a static variable that holds the reference to the single instance. You initialize it as
soon as you load it. A static method returns that instance. The code is really very simple.
You can find it in the sample project Singleton_4:
private MySingleton() { }
Since no second instance of the class is to be created and cannot be created at all, the
following solution (in the Singleton_5 example project) is also conceivable: Make the
instance public, and you then additionally save the getInstance method:
private MySingleton() { }
You avoid all problems resulting from concurrency with this approach. But you have to
trust that the virtual machine will first create the instance before allowing other threads to
access it – and that is exactly what you can rely on according to the specification.
Nevertheless, there are three things to keep in mind here as well:
1. The instance is created when the class is loaded. Assuming you have an extensive ini-
tialization routine, this will be run through in any case, even if you do not need the
singleton afterwards.
2. You have no chance to pass arguments to the constructor at runtime.
3. The singleton pattern ensures that the class is instantiated only once per class loader. In
the case of a web server, it may be that the class is loaded in more than one class loader
within a virtual machine; then you have more than one instance of the singleton class
again. Without going into detail, such situations are nowadays dealt with using
Dependency Injection.
In Fig. 3.1 you can see the (quite simple) UML diagram for the Singleton from the sample
project Singleton_4, i.e. with the getInstance method.
3.7 Antipattern
You now know the Singleton Pattern and how to implement it. You know the advantages
and disadvantages of the different implementations. But there are fundamental objections
against the Singleton Pattern. I mean objections to the pattern as a definition and not to the
realization shown. Search for the term “evil singleton” in your trusted search engine. I get
over 2,000,000 hits.
• Each class should focus on exactly one task: Single Responsibility Principle. Singleton
violates this principle in that the class must take care of both the business logic and its
own object creation.
• Just as with global variables, the dependency is not immediately obvious. Whether a
class makes use of a singleton class cannot be seen from the interface, but only from
the code.
• The coupling is increased.
• Too extensive use of singleton tempts the programmer to program procedurally.
• If the instance has its own attributes, these are available in the complete application. It
is questionable whether there is data that is actually needed “everywhere”: in the view,
in the controller, in the model and in the persistence layer.
• You restrict yourself when using singletons: You cannot create subclasses of singleton
classes. If 1 day you do need a second instance, you have to rewrite the code again.
• Singletons create something like a global state. This makes the test procedure more
difficult for you.
3.8 Summary
• A singleton is used when you have a class of which there may be only one instance.
• There must be a global access point for this one instance.
• Lazy instantiation creates the instance when it is needed.
3.9 Description of Purpose 31
The Gang of Four describes the purpose of the pattern “Singleton” as follows:
Ensure that a class has exactly one copy, and provide a global access point to it.
Template Method
4
The pattern “Template Method” belongs to the behavior patterns and is quite catchy. It
helps you when you need to implement an algorithm differently in subclasses, but on the
other hand must not change it.
Take the classic order of input, processing, and output as an example. You want to input a
string, convert it optionally to upper or lower case, and then output it to the console.
At first glance, the task seems to be solved quickly if you define two classes: Uppercase
and Lowercase. I print the source code of Uppercase below. The rest can be found in
the sample project Template_1. The method run() defines the algorithm: input, convert,
output. The source code of the Lowercase class is identical. Only the bold part differs.
Now we build an abstract superclass that defines the algorithm on the one hand, and the
methods that are identical in both classes on the other. Since the definition of the algorithm
should be binding for all subclasses, it makes sense to declare the method run() final.
You can find this code in the sample project Template_2.
The client can now easily select the desired converter by instantiating it from the appropri-
ate subclass:
The call is always made from the superclass. The algorithm is defined in the superclass
and the superclass calls the corresponding actual functionality in the subclass. This proce-
dure is what GoF calls the Hollywood principle: “Don’t call us – we’ll call you!”
Also note that at this point (highlighted in bold above), the declaration of the vari-
able input does NOT work with a var. We want to have a local variable of the
superclass type here, so that we can then assign instances of the various subclasses as
we wish.
If you use var. at this point, the compiler recognizes the type LowerCaseConverter and
assigns it to the variable. Until then everything is ok. But if you later want to build an
UppercaseConverter with the same variable, you run into an error. Fortunately, however,
already at compile time.
36 4 Template Method
You can extend the project with hook methods. A hook method is a method that can
optionally be overridden. A default behavior – or no behavior at all – is defined in the
superclass. The subclasses can – but do not have to – adopt this behavior. In the Template_3
sample project, there is such a hook method. The save() method returns whether the
entered text should be saved to disk. In this case the method saveToDisk() is called.
By default false is returned.
// … abridged
If the subclasses feel that the default behavior doesn’t fit this way, they are free to over-
ride it. For example, the UppercaseConverter wants the text to always be saved – so
it returns true:
@Override
protected boolean save() {
return true;
}
}
4.2 The “ListModel” Interface 37
The LowercaseConverter wants to let the user decide whether to save the text:
@Override
protected boolean save() {
var question = “Should the text be saved?“;
var answer =
JOptionPane.showConfirmDialog(null, question);
return answer == JOptionPane.YES_OPTION;
}
}
Hook methods are also called insertion methods. They offer the possibility to influence
the flow of the algorithm.
That’s it – now you know the Template Method Pattern. Despite – or perhaps because
of – its seemingly trivial formulation, the Template Method Pattern is extremely important
for the development of frameworks such as the Java class library. You develop a basic
framework, the algorithm, and build your extensions on it.
You will be able to identify the Template Method Pattern wherever you encounter abstract
classes. At this point, I’d like to briefly discuss the algorithm that the ListModel inter-
face specifies; it describes what must happen and what data must be present for a JList
to display data. A JList accepts any object as a data model that is of type ListModel.
The ListModel interface prescribes four methods that are required to display data. A
JList must be able to register and deregister with the model as an observer, a
ListDataListener; to do this, the addListDataListener() and removeL-
istDataListener() methods must be implemented. The JList must also be able to
ask the database how many items to display; this is answered by the getSize() method.
And finally, the JList must be able to query the element at a specific location; for this,
there is the getElementAt() method, which you pass the index of the element you are
looking for.
In the directory of this chapter, you will find the sample project ListModel_Example.
This project demonstrates the procedure with a very simple example. If you analyze its
source code, you will see that the methods for registering and deregistering listeners are so
general that they can probably be used in all situations.
38 4 Template Method
Fig. 4.1 UML diagram of the Template Method Pattern (example project Template_3)
In the class library there is the AbstractListModel class in which these two meth-
ods are implemented. If you need a different implementation than the default, you are free
to override these methods. Methods that describe the database, that is, getSize() and
getElementAt(), escape a standard implementation. You must always define these by
yourself. Please have a look at the AbstractListModel_Example project again.
The UML diagram of the Template Method Pattern for the example project Template_3
can be found in Fig. 4.1.
4.4 Summary
The Gang of Four describes the purpose of the Template Method pattern as follows:
Define the skeleton of an algorithm in an operation and delegate individual steps to sub-
classes. Using a template method allows subclasses to override specific steps of an algorithm
without changing its structure.
Observer
5
You will now learn a pattern that is similarly easy and intuitive to understand as, for
example, the Singleton or the Template Method pattern: the Observer pattern. The idea is
that you have an object whose state changes (event source); any number of other objects
(observers or listeners) want to be informed about these state changes.
5.1 Introduction
The sample project Observer_01 demonstrates a first realization of the pattern. You have some-
one who wants to announce news, an event source, in the example a job market. This class is
able to register objects of the type JobObserver as interested parties, or observers; this type
is defined by the interface JobObserver, which prescribes the method newOffer().
The event source, i.e., the job market, defines three main methods: one to register
observers and one to deregister them. Also, a method to save a new job and inform all
observers about it. Saving jobs is not actually required in the sample implementation. But
if you want to add evaluations about the reported jobs in the job market, for example, you
will of course need it.
You can see in the code above that the information to all observers runs through the
forEach method. And for each tempJobObserver from this list, the newOffer() method
assigned via the lambda expression is then called. You can of course also use a common
for loop at this point:
It provides the same functionality. The difference is that the for loop is an external itera-
tor (which you write), while the forEach method is an internal iterator (which you only call
because it’s already built in). forEach also works with sets, queues, and maps, by the way.
The observers define what happens when the method newOffer() is called. The
parameter of this method is the new job. In the example, the method randomly decides
whether the observer applies for the job or not. Below you can see the abbreviated listing
for the Student class. In addition, you will also find the class Employee in the project;
5.3 Extending the Approach 43
this is to model employees who are looking for a part-time job. They, too, must implement
the JobObserver interface and thus their own version of the newOffer() method. In
the example, I only made a small difference: students apply for a new job with 80% prob-
ability, employees only with 50% probability. After all, they already have a job.
The test program, which you will find in the project, creates two JobObservers and
tests which of the two is informed about a new job under which circumstances. The
expected console outputs can be found in the comments of the main method of the
test driver.
Three things are certain in the life of a programmer: death, taxes, and changes in the
requirements of one’s software. In this project, too, the requirements will grow.
Tip
To maintain the greatest possible flexibility, always program against interfaces,
never against implementations. Please note: In the language of design patterns, the
term “interface” is not to be equated exclusively with an interface, and certainly not
with interfaces in the sense of data transfer or function calls between systems. When
we talk about an interface, we can also mean an abstract class. Consequently, you
can “extend” or “implement” an interface, which describes the same process in the
patterns language.
This expression has become customary. It would certainly be more appropriate to
speak of “assuming a role” instead of “implementing an interface” – the Student
class could then assume the role of a JobObserver.
44 5 Observer
You have already seen in the previous project Observer_01 the advantage that the job
market has no idea what kind of objects it registers. For them it is sufficient that the objects
are of the type JobObserver – these can be students or employees.
What change could there be? It could be that students should not only be able to register
with the job exchange of the university, but also with a job exchange of the employment
agency. In the sample project Observer_02 you will find the interface JobProvider,
which is implemented by the event sources. For the client, in our case the main method of
the test class, it can be useful under certain circumstances not to be bound to a concrete
implementation (job exchange or employment agency), but to be bound in general to
objects of the type JobProvider. It can now exchange the concrete implementations
and, for example, post a job with both providers at the same time:
// … abridged
Again, I use the type inference with “var”. No matter if it says “JobMarket job-
Market = new JobMarket();” or “JobProvider jobMarket = new
JobMarket();” or “var jobMarket = new JobMarket();”, it will always
create an object of type JobMarket. You can easily check this with a “System.out.
println(jobMarket.getClass());” interspersed in between your code. As you
can see, type inference also works with fields. The field with the two job providers is also
created correctly.
A variation of this example is still conceivable: In the previous two examples, you
passed a job as a string and passed it to the observers. This procedure is called push
method. It is also possible that the event source only informs the observers that new infor-
mation is available, without passing it immediately. It is then up to the observer to request
the complete information from the event source. This procedure is called pull method.
In the example project Observer_03, the students and employees are not looking for
jobs, but for apartments. Apartment offers are very extensive objects with their exposés
and floor plans, which are only sent on request. The observers first decide randomly
whether to request the information at all. Then they decide randomly whether they are
interested in the apartment. Please analyze the project on your own.
5.5 Concurrent Access 45
In the Java class library, there is the interface Observer. This interface defines the role
of an observer and prescribes the update() method. An object of type Observable
and an object of general type Object are expected as parameters. A link to the event
source is passed to the method to enable the observer to request further information from
the event source. In addition, the event source can be queried for its data type (method
getClass()). The parameter of type Object contains, for example, a job or an
apartment.
The event source is therefore of the type Observable. Usually the suffix -able
indicates an interface, but not here. Observable is a class that can be extended. As you
are used to from your own implementation, there are methods in the Observable class
to register and deregister Observers. Unlike your implementation, before you inform
Observer, the setChanged() method must be called, which sets an internal flag. Only
when this flag is set does the notifyObservers() method actually communicate the
changes to the observers. This procedure can be useful if many changes are made to the
database, but the observers are not to be informed until all changes have been completed.
However, the interface Observer and the class Observable are marked as “depre-
cated” and should no longer be used if possible. It is not type-safe and also not thread-safe.
For special Observer variants, the class library contains the package java.
util.Flow, which is designed for thread-safe parallel processing of data and in which
you will find the interfaces Publisher and Subscriber. Together with an execu-
tor and a CompletableFuture, the processing of many individual data packets from
a source can be divided among parallel threads, which can then request their respective
workload themselves. The code for this is very complex and beyond the scope of this
book. However, you will find plenty of comprehensible examples on the Internet.
On the other hand, in the programming of user interfaces, events are triggered by the
user to which the application must react: Pressing buttons, clicking with the mouse on but-
tons or menu items, or even inserting data into lists, which then have to trigger an update
of the user interface again. Especially for the latter, there are also special observable inter-
faces for maps, sets, arrays or lists in the JavaFX library since version 2.0, e.g. javafx.
collections.ObservableList.
We’ll look at thread-safe observers in the next section, and we’ll look at user interfaces
after that as well.
The solution you saw in the Observer_03 project works flawlessly. But if you want to work
with it on a concurrent system, you run into problems. Imagine that a thread is handing
over a new flat and all observers are informed about it. At the same time, another thread is
46 5 Observer
trying to unsubscribe an observer. There are then two threads that want to access the list of
observers at the same time. Open the sample project Observer_04. I have reduced it to the
most necessary classes so that you have, for example, only workers and no students. Start
the main method of the class ObserverSim. This version starts two threads (ApartmentThread
and DeRegisterThread) shortly after each other, both of which can work with the list of
observers. The described situation, that two threads want to access the database at the
same time, is provoked here deliberately. A concurrentModificationException is thrown
after a short time. You may have to start the main method several times.
Another problematic situation can arise when observers are informed about a change
and decide to unsubscribe as observers. This situation can be found in the example project
Observer_05. In the update method, a random decision is made whether the observer still
wants to be informed:
@Override
public void updateFlat(ApartmentMarket provider) {
var random number = (int) (Math.random() * 10);
if (random number <= 6) {
var apartment = provider.getDetail();
if (random number < 5) {
// … abridged
provider.removeObserver(this);
}
}
// … abridged
}
Fig. 5.1 Concurrent access can throw an exception. (Screenshot from NetBeans)
5.5 Concurrent Access 47
meantime, it causes the method removeObserver to manipulate the list, which is being
used by the iterator, which may have to notify other observers. And that’s where the excep-
tion hits.
In the upcoming sections, I will show you how to avoid difficulties from concurrency.
// … abridged
Does that solve all the problems? No! Observers cannot log in or log out as long as
information is transmitted to observers. This solves the problem from project Observer_04.
Project Observer_05 would still throw an exception. You also need to imagine the follow-
ing situation: You are in the process of informing all your customers about an important
new feature. Since the information is very extensive, it takes probably half an hour until
the list of all observers is processed; it would be unpleasant if no new customers could
register in this time. So, there must be another solution. By the way, the Observable class
I introduced in Sect. 5.4 synchronizes the list of observers in exactly this way.
48 5 Observer
The sample project Observer_07 shows you a different solution: The list of observers is
copied before the notification.
This blocks the list only for a short moment. When you send the notifications, the cop-
ied list is always informed; if an observer logs in or out of the original list at the same time,
there is no conflict in access. The disadvantage of this solution is, of course, that the
observer list has to be copied more or less effortfully with every update.
At this point, by the way, I use a function that was added in Java 10, along with a few
others: List.copyOf(). This is the easiest and fastest way to copy a list. The syntax is very
simple, and the same functions exist for the Set and Map classes: Set.copyOf() and Map.
copyOf().
If you test the project, you will also notice that an observer who has just logged off will
still be informed again – to do this, run the main method a few times (see Fig. 5.2).
If you do not want to copy by yourself, you can also use a thread-safe
CopyOnWriteArrayList. The sample project Observer_08 demonstrates the use of
this class.
You take a different approach when you wrap the event in another class; it’s like putting a
letter in an envelope and mailing it. The serializable class EventObject from the pack-
age java.util expects a reference to the object that sends the event as a parameter in
the constructor. The getSource() method returns this source. The toString
method from the Object class is meaningfully overridden. The interface
EventListener from the same package does not prescribe any methods, but only
defines a role.
In the ListenerDemo sample project, there is a JobEvent class that extends
EventObject. It expects the source and a string object that describes the new job as
parameters. Of course, you could also provide a flat here instead of the string. The source
is passed on to the Super class, the job is stored in its own data field.
50 5 Observer
@Override
public String toString() {
return job;
}
}
The JobAgency class maintains a list of JobListeners. When a new job is posted, the
class generates an event of the type JobEvent and transmits it to all JobListeners.
// … abridged
}
Let’s look at how this variant can be used practically in the next section.
5.7 Listener in GUI Programming 51
If you look at the EventObject class in the API documentation, you will see that many
event classes that are needed in GUI programming are derived from it: ListDataEvent,
ListSelectionEvent, AWTEvent, and quite a few more. One event, the
TableModelEvent, I would like to introduce now. In the last chapter I briefly showed
how a Jlist queries its data from the database. The interaction of a Jtable and its data-
base is similar: You have a database that you need to describe to the Jtable so that it is able
to display the data. The bridge between the two is the TableModel interface. The Jtable
expects an object of type TableModel, which you pass to it using the setModel()
method. With this object, the Jtable registers itself as a listener, which is why you must
implement the methods addTableModelListener() and removeTableModel-
Listener(). You use getColumnCount() to return how many columns to display.
The getColumnName() method returns the heading of a column. Since you can return
any data type, getColumnClass() tells you what class an object in a particular column
is based on. The getRowCount() method is equivalent to the ListModel’s get-
Size() method; it returns the number of rows in a column. A cell in a table can always be
edited; if you want to prevent this, the isCellEditable() method must return a false
value. The methods getValueAt() and setValueAt() describe on the one hand
which value is contained in a certain cell, on the other hand how the database should react
when a cell has been edited. Each cell is uniquely defined by its column and row. In the
MVC_1 sample project, the set jobs are displayed in a table (Fig. 5.3).
To generate this display, there are two classes, a display and a database. The database
describes the data and the column header in a way that is understandable for the Jtable
class. In addition, there are two methods that define what happens when a new job is
posted. The addJob() method is passed a string containing the new job; it adds the job
to the job list and calls the private fireStateChanged() method. This method gener-
ates a TableModelEvent and passes it to the listeners, which then update their display.
// … abridged
}
How is the data displayed? The display class builds a JFrame and passes an instance
of the DataBase class to the JTable as a table model. Furthermore, it provides the method
addJob(), which receives a job and passes it to the database.
Display() {
var pnlDisplay = new JPanel();
pnlDisplay.setLayout(new BorderLayout());
var tblJobs = new JTable();
tblJobs.setModel(jobModel);
pnlDisplay.add(new JScrollPane(tblJobs),
BorderLayout.CENTER);
mainFrame.getContentPane().add(pnlDisplay);
mainFrame.setSize(500, 500);
5.7 Listener in GUI Programming 53
mainFrame.setLocationRelativeTo(null);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setVisible(true);
}
The main method of the Display class adds a new job to the display instance every
second. I refrain from printing, please analyze the sample code for yourself.
// … abridged
54 5 Observer
The main method of the start class again creates some jobs and passes them to the
database:
The last change concerns the data model. The array with the column headers has
become larger. If you want to output the value of a specific cell, first have the job in this
row returned. Then query the relevant value depending on the column.
@Override
public Object getValueAt(int rowIndex,
int columnIndex) {
Job job = jobList.get(rowIndex);
return switch (columnIndex) {
case 0 -> job.description;
case 1 -> job.salary;
case 2 -> job.extra == null ? “” : job.extra;
default -> job.description;
}
}
// … abridged
}
5.7 Listener in GUI Programming 55
Another important change relates to the specification of the data type of a column. The
second column (index = 1) is of type double. All other columns are strings. By default,
a double value is displayed by JTable right-justified, strings left-justified.
@Override
public Class<?> getColumnClass(int columnIndex) {
return switch (columnIndex) {
case 1 -> Double.class;
default -> String.class;
};
}
As you can see, the effort to describe the database increases with the number of col-
umns. However, it is not difficult to implement a TableModel.
In the two program excerpts above you can see another Java novelty compared to Java
8: Switch Expressions. They have been available as a preview feature since Java 12, were
supplemented by the yield keyword in Java 13 (which I will show you in a later chap-
ter), and have been included in the Java standard since Java 14.
Take another close look at the two examples in bold. Several things stand out at
first glance:
1. Switch is now available as an expression, in both cases now in the return state-
ment of a method. But they can just as well be on the right side of an assignment or
a comparison.
2. The colon in the switch statement is now replaced by an arrow (as in lambda expres-
sions). Behind it in the two examples above is then exactly what would also be on the
right side of an assignment or in a comparison.
3. There is no break (and also no return). Unlike the switch statement, the processing
in the switch expression ends at the end of the respective expression for the selected
case. The processing does not “fall through”.
4. The default case – in the first code snippet – is identical to one of the normal cases, but
must still be in its own case distinction.
The switch statement still exists. But with the newly added switch expressions, suitable
code can be formulated much more elegantly and readably. It is also possible to process
several statements in one case, which are then combined with curly brackets. To define the
return value of the expression in this case, the yield command (instead of a return
or a break) is used.
For the case distinction of enum values the compiler checks whether all values are
either checked or alternatively a default case is available. Conversely, this means that the
default case can be omitted when explicitly checking all cases. For all other types in the
case distinction, however, a default case must be specified.
56 5 Observer
Why have I described the TableModel here? In very few Java introductions I find
explanations of the interface TableModel. Most of them describe the class
DefaultTableModel. Maybe that’s the reason why the TableModel leads a rather
shadowy existence in practice. In practice, you will much more often find table models that
rely on the DefaultTableModel. It is better documented and – at least at first glance –
easier to use. In fact, however, the class is not without its problems. To name just one criti-
cism, it uses vectors for the internal representation of the data – and that costs performance
unnecessarily. I am therefore promoting the idea of developing your own table models on
the basis of TableModel, and hope to be able to interest you a little in this.
The MVC_1 project leads to a discussion about the MVC pattern. MVC stands for model,
view, and controller. Each of these entities has clearly defined tasks: The Model contains the
data, the View is responsible for displaying it on the screen, and the Controller mediates
between the two. The user – in the example the main method - always accesses the control-
ler. If the data basis changes, it informs the logged-on listeners, the view units, of the change.
The view then queries the data from the data basis and thus brings itself back into a consistent
state. All components, for example JList and JTree, work according to this principle.
What is gained with this solution? You can register any number of observers –
views – with the data model. Both data model and view are independent of each
other and can be exchanged.
Where can the MVC model be verified in the MVC_1 project? The database, i.e. the
model, is the TableModelObject with its job list. The Display class uses an instance
of the JTable class to display the data on the screen. One might now be tempted to define
the class JTable as a view. However, this assumption is wrong. But why?
5.10 Summary 57
Background Information
In the directory of this chapter, you will also find the sample project MVC_2. Here,
the classes Student and Employee reappear, which you still know from further
above. Both implement the JobListener interface, which extends the
TableModelListener interface. So Student and Worker take on the role of the
View and must override the tableChanged() method. I won’t go into any more
detail about the code; it’s not difficult to understand.
Most books on design patterns – including the GoF – describe a statistic (the
model) in the Observer Pattern, which is displayed once as a pie chart and then as a
bar chart (view). In my example, the database is the job board, which is displayed by
two different views. However, the principle is the same.
In Fig. 5.5 I show you an example of the UML diagram for the sample project Observer_08.
5.10 Summary
• The Observer Pattern is always used when the status of an object changes and any num-
ber of other objects are to be informed of this.
58 5 Observer
Fig. 5.5 UML diagram of the Observer Pattern. (Sample project Observer_08)
The Gang of Four describes the purpose of the “Observer” pattern as follows:
Define a 1-to-n dependency between objects so that changing the state of one object causes
all dependent objects to be notified and automatically updated.
Chain of Responsibility
6
Imagine you want to apply for a driver’s license. So you go to the first room in the corridor
at the citizen center. There you ask whether you can apply for a driving license. The offi-
cial in the first room does not process driving licenses, but hunting licenses, so he is not
responsible; therefore he sends you to the second room. There, too, you ask and either get
help or are sent one room over. At some point you will meet an officer who is responsible
for driving licenses and he will help you.
This already makes one feature of the pattern clear: you have a system of objects that
could all potentially process the message.
Your vision of the future is taking shape: You have a refrigerator with an RFID receiver.
The fill level of your beer bottles is checked regularly. Even if the number of slices of
sausage and cheese drops, it will be registered. And if eggs and bread are running low, the
fridge will know that too. Of course, since you’re a motivated programmer, you don’t have
time to go to the supermarket to shop yourself. Fortunately, stores in the area have banded
together to form an electronic supply chain. When refrigerators report that a customer is
running low on a product, each of the retailers checks to see if he can bring the customer
new food. If not, they pass the order on to the next possible retailer.
In the first step, you define which foods are to be supplied by the electronic supply chain.
In the example project CoR_1 this should be bread, cheese, sausage, eggs and beer. You
define these goods in an enumeration.
In this project, the job, the “problem”, is passed through a variable from the enumeration.
Do you remember the Observer pattern? There you used a listener and an event object. This
principle also fits with the Chain of Responsibility: you create an event object and pass it to
the first link in the chain. This link decides whether it can process the event; if not, the event
is forwarded. I will show you below that the Java class library does exactly this.
In the next section you will get to know the sellers of the goods.
As traders I define the beverages store, the bakery and the farm shop of Old McDonald,
where you can get fresh cheese, sausages and eggs. All merchants must have identical
methods in addition to their actual tasks (purchasing, manufacturing, etc.): sell and pass on
the order if it cannot be served. So you define an abstract superclass that holds a reference
6.2 First Code Example: Grocery Shopping 63
to the next merchant in the chain. You call the setNext() method when you want to add
another merchant to the chain. Each dealer first checks whether it itself already references
a following dealer. If so, it will not store the new merchant, but pass it to the next mer-
chant, which will now check if it is the last one in the chain. If so, it will save the new
merchant as the following merchant. In addition, each trader must also provide a sell()
method – you declare this in the superclass, but don’t define it yet.
As an example of a specific merchant, I am only printing the bakery here. The other
merchants can be found in the sample project. The sell() method is representative of an
arbitrarily complex problem solution. However, the principle behind it is always the same:
If I can solve the problem, I solve it, otherwise I pass it on.
@Override
public void sell(Groceries article) {
if (article == Groceries.BREAD)
System.out.println(name + “ sells “ + article);
else
forward(article);
}
}
Food is defined, the dealers are ready. The test class is still missing.
64 6 Chain of Responsibility
The main method of the test class is the client that demonstrates how to handle the chain.
First, you create any number of merchants and link them:
Now the client can pass a purchase order to the first link in the chain. The order is now
passed on to a dealer until one recognizes his responsibility and delivers the desired goods.
A trader now sells the desired goods until either he himself can no longer offer any or
the quantity demanded is zero. The question of whether he has any goods at all is decided
by a random generator. Using the example of the bakery, let’s look at the changes.
So each link in the chain can now modify the request – in this case: the purchase.
Where can this pattern be demonstrated in Java? Take a look at the example project CoR_3.
The main method builds a new window. An instance of the class JFrame contains an
instance of the class JPanel and this in turn contains an instance of the class JLabel.
6.3 An Example from the Class Library 67
Furthermore, there is an EventListener that, when a component is clicked with the mouse,
outputs on the console which component was clicked.
myLabel.addMouseListener(adapter);
myFrame.addMouseListener(adapter);
myPanel.addMouseListener(adapter);
Run the program and observe which source is output each time you click the window,
panel, or label. If you click the label, the label is named as the event source. If you click
on the panel, the panel is and in the other cases, the window.
Comment out the first line so that no event handler is assigned to the label. If you now
click on it at runtime, the panel is output as the event source. Each component checks its
responsibility and passes the event on to the surrounding container if necessary – until the
window can no longer hand over its responsibility. A second characteristic of the pattern
can be seen here: Processing goes from the specific to the general, from the leaves to the
root. You will get to know the composite pattern further on, where the processing goes
from the root to the leaves, i.e. vice versa.
The example project CoR_4 goes one step further. The GlassPane of the window is
activated and gets the EventListener assigned.
Now it doesn’t matter where you click – it’s always the GlassPane that processes the
MouseEvent. In practice, you can intercept user input and mouse clicks this way, for
example, when your program is performing a large calculation. You effectively cover the
user interface with the GlassPane to protect it from unwanted access. Once you set the
“visibility” of the pane back to false, the covered interface is accessible again. The
GlassPane is an object of type JPanel, so on the console this type is always named as the
event source. Now this example doesn’t really add anything new to the theme of the pat-
tern, but it’s a handy tip that I like to put here.
68 6 Chain of Responsibility
Figure 6.1 shows the UML diagram for the example project CoR_2.
Fig. 6.1 UML diagram of the Chain of Responsibility Pattern (example project CoR_2)
6.6 Description of Purpose 69
6.5 Summary
The Gang of Four describes the purpose of the Chain of Responsibility pattern as follows:
Avoid coupling the trigger of a request to its receiver by allowing more than one object to
complete the request. Chain the receiving objects and route the request along the chain until
one object completes it.
Mediator
7
The mediator pattern enables the flexible interaction of several objects/classes that may
not know each other. The mediation function can be varied very flexibly, but can also lead
to very extensive implementations. In contrast to the Observer or the Chain of Responsibility,
the Mediator also has its own tasks to perform. This gives him a central role. However,
there is also the danger that it becomes very extensive and even confusing.
To start with, I would like to contrast Mediator and Observer. The purpose of Observer is
to relate an event source to any number of observers. The relationship was uni- or bidirec-
tional: The event source knows the interface of the observers and maybe vice versa.
However, it becomes problematic when each observer also wants to be an event source for
every other observer. This might be the case, for example, if you are programming a chat.
There are an unspecified number of participants, all of whom are interested in the events –
messages – of the others. If you were to program a chat room using the Observer Pattern,
each participant would have to keep a list of all the other participants. New participants –
in the Mediator Pattern, we talk about colleagues – would have to register with all existing
participants and in turn reference all existing participants. Extrapolate how many relation-
ships you have with – say – 33 participants, i.e. colleagues. You need a different approach
for that.
If 33 colleagues all knew each other, you would have an inefficient spider web of over
1000 relationships. The mediator now has the task of decoupling event sources and observ-
ers. He mediates between the participants.
The mediator is scalable. I would like to create the following scenario as an illustration:
You can have a class in your program that manages the orders of an online shop; but
instead of, or even in addition to, this class, the “Orders” unit can also be a whole depart-
ment of employees who enter additional orders in the telephone service. The “Orders” unit
reports a new order for processing to the mediator – which can again be either a single
class or a department of employees.
The mediator asks customer management – which again can be a class or a whole
department – whether the customer is already registered. If not, the mediator asks cus-
tomer management to create a new record; if the customer is already on file, the mediator
asks accounting (you know: class or department) whether the customer has reliably paid
his bills. Depending on the accounting department’s answer, the mediator instructs the
shipping unit to ship the goods either by invoice or by cash on delivery. Now, if 1 day there
is a need to add another unit to the system, only the mediator needs to be adjusted. Let the
case arise that the head department wants to be informed of all purchases above a certain
sales value. All you have to do is create a message in the mediator; the other units would
not know anything about it.
The following simulation shows you the mediator in action. You have one unit that pro-
duces wine; this unit is a simplified producer, in reality winemakers, cooper and the glycol
industry are behind this unit. Another unit represents costumers or retailers, the consum-
ers. Consumers buy wine at the respective low price. Since there are x consumers and y
producers, the dependencies are reduced to the middleman, the mediator. The mediator
receives the consumer’s requests and forwards them to the producers. Finally, the produc-
ers tell the mediator what price they want; the mediator forwards this information to the
consumers. In addition to a method for negotiating the price, the mediator needs other
methods for registering and unregistering consumers and producers. The sample code can
be found in the WineSim project.
A consumer must have a method with which it can register with the mediator; I will dis-
pense with the possibility of de-registering so as not to inflate the example unnecessarily.
7.3 Mediator in Action – An Example 73
In any case, the consumer must have a method to request wine. This method is given the
quantity of units – for example bottles:
A consumer registers with the mediator and submits its request to the mediator.
// … abridged
@Override
public double requestPrice(int quantity) {
System.out.
println(name + “ requests ” + Quantity
+ “ bottles of wine.“);
double totalPrice = mediator.getQuote(units);
return totalPrice;
}
}
In the next section, let’s take a look at the producers of the wine.
Producers must provide two methods – one for registering with the mediator and one for
the mediator to address his requests to.
A producer calculates the price for a unit on the basis of a random number and depend-
ing on the requested number of units and communicates this price to the mediator.
@Override
public double getQuote(int quantity) {
double discountFactor = 1.0;
if (quantity > 100)
discountFactor = 0.7;
else if (units > 50)
discountFactor = 0.8;
else
discountFactor = 0.9;
double price = Math.random() * 9 + 1;
price *= discountFactor;
String strPrice =
NumberFormat.getCurrencyInstance().format(price);
System.out.
println(″Producer ″ + name + ″ asks ″
+ strPrice + ″ per bottle.“);
return price * units;
}
}
Note that here, although the producer quotes the price per bottle (outputs it to the con-
sole) when submitting an offer, he then returns the total price to his caller. Of course, the
mediator has to take this into account in his calculation. We’ll deal with that in the next
section.
// … abridged
@Override
public double getQuote(int quantity) {
List<Double> quotes = new ArrayList<>();
In the example, the list of consumers is not currently used by the mediator. But you can
think about what you need to add if a producer wants to use the mediator to request the
addresses of customers to send an advertisement (assuming the customers have given their
consent). Now we can test the project.
76 7 Mediator
How can you test the interaction of the classes? You create any number of consumers and
any number of producers and register them all with the mediator. Then you let the consum-
ers request the price for a certain number of bottles. The mediator asks all the producers
for quotes, determines the cheapest one, and forwards it to the consumers.
unit = 10;
price = jack.requestPrice(quantity);
}
}
The mediator pattern is also very common in the implementation of user interfaces. In the
model-view-controller model, the controller is precisely the mediator that mediates
between the view/use and the abstract model of an application. Let’s look at an example
of this.
You have a GUI that shows two lists. The left list shows all contacts in your address book;
the right lists all contacts who will be invited to the next party. For this example, we need
four buttons. One button moves a contact to the list of invited contacts, another button
moves a contact back to the general list of all contacts. A third button deletes a selected
contact and the fourth button exits the program. The screenshot in Fig. 7.1 shows how the
finished program should look like. The source code can be found in the project
SwingExample.
Various dependencies are to be defined:
• The Exit button is always activated, regardless of the dependencies described below. If
you click on it, the program is terminated.
• If a contact is marked on the right list, the button Do not invite is activated. Clicking this
button moves the contact to the left list.
• If a contact is selected on the left list, the Invite button is activated.
• When the user clicks on this button, the selected contact is moved to the right list.
• Only one contact can be selected in both lists.
• The Delete button is activated when an entry is selected in one of the lists.
• If no contact is selected – neither in the right nor in the left list – no button – except
Exit – is activated.
• When a contact has been moved or deleted, all markers are cleared and all buttons are
disabled except for the Exit button.
I print the source code of the project only in abbreviated form and limit myself to the
essential key points. Please analyze the source code further on your own. The two lists are
instances of the class JList. The four buttons are instances of the class JButton. All
components register with an instance of the class Mediator, which is the core of the proj-
ect. It stores references to all components involved. It also defines methods that are called
when an event is fired. Let’s look at the flow using the Invite button as an example. When
the Invite button is activated, the Invite() method of the Mediator is called. Within this
class, all the buttons – except for the exit button – are first disabled. Then the method gets
the models of the two JList instances and moves the selected entry into the list of
invited contacts.
class Mediator {
private JButton btnInvite;
private JButton btnDisinvite;
private JButton btnDelete;
7.4 Mediator in Action – The Second Example 79
// … abridged
void invite() {
btnInvite.setEnabled(false);
btnDelete.setEnabled(false);
btnDisinvite.setEnabled(false);
var selectedItem =
(String) allContactsList.getSelectedValue();
tempModel = invitedContactsList.getModel();
var invitedContactsModel =
(InvitedContactsModel) tempModel;
allContactsModel.removeData(selectedItem);
invitedContactsModel.addData(selectedItem);
allContactsList.clearSelection();
invitedContactsList.clearSelection();
}
}
The Invite button is deactivated after it is created and is registered with the Mediator.
The ActionListener passed to the button provides for the Invite() method of the Mediator
to be called when the button is activated.
Note that I’ve used lambda notation here. This saves me from typing …new
ActionListener and @Override public void actionPerformed … and
makes the code much easier to read.
In this way, all components are created and positioned on the GUI. Each component
registers with the Mediator and calls a specific method there.
For the example project WeinSim you can see the UML diagram in Fig. 7.2.
Fig. 7.2 UML diagram of the Mediator Pattern (example project WeinSim)
7.8 Description of Purpose 81
Let’s take a critical look at the project SwingExample. What stands out? On the one hand,
as with the wine trading simulation, you can see the advantage that the colleague classes
involved don’t know each other – and don’t need to know each other. They are loosely
coupled, which is always an indication of a good design. You can insert new colleagues at
any time – the changes to the source code are limited to the mediator.
On the other hand, the disadvantage should not be concealed. If you analyze the
Mediator class, you will find a relatively large class. And this is exactly the danger of the
mediator: you risk developing a “god class”1 that grows rapidly and becomes confusing as
the number of components involved increases.
7.7 Summary
The Gang of Four describes the purpose of the “Mediator” pattern as follows:
Define an object that encapsulates the interaction of a set of objects within itself. Mediators
promote loose coupling by preventing objects from explicitly referring to each other. They
allow you to vary the interaction of objects independently of them.
1
“God-class” is what it’s called because only the good Lord still keeps track of the code.
State
8
The State Pattern encapsulates state expressions in objects. This can be useful when an
object shows different behavior depending on its state. Think of a garage door. The door
can be open, but it can also be closed. You can open a closed door; however, there is little
point in trying to open an open door. But how can you prevent someone from trying to
open an open gate or close a closed gate? The state pattern solves this problem by repre-
senting each state by its own object; as a result, everyone can only do what is supposed to
be allowed in the current state.
Let’s first digress and look at the Enum Pattern before we tackle the State Pattern.
If you want to represent two state expressions, you quickly get the idea of representing
these state expressions by numerical values and passing these values to final variables.
Within a switch statement you query the values and react to them:
void printState() {
switch (state) {
case OPEN ->
System.out.println(“Gate is open“);
case CLOSED ->
System.out.println(“Gate is closed“);
default ->
System.out.println(“*** Error ***“);
}
}
// … abridged
}
In this code section, pay attention to the Switch Statement(!), where I use the arrow
syntax after the respective case, which was inserted with Switch Expressions (compare my
first explanations of Switch Expressions in Sect. 5.7). Switch statements also benefit from
this syntax innovation, because I save the break commands here.
When you create an object of this class, you can either pass one of the defined constants
to the setState() method, or you can pass an arbitrary int number by error. Depending
on the state, a different text is output to the console in each case. In the example project
GateManager you will find the classes GateManager_1 to GateManager_5 described
above or in the following. For the sake of simplicity, I have combined them into one
NetBeans project. In GateManager_1 you will find a main method within the class that
demonstrates the procedure described above.
An alternative approach is to represent the state characteristics by objects of their own data
type. To do this, declare a class State. Within this class, two static final variables of this
type are created. To prevent the user from creating further objects of the type state, the
constructor is declared private – access is now only possible within the class. The tech-
nique is the same as you have already seen with the Singleton pattern. You can find said
data type as a static inner class in the GateManager_2 class.
// … abridged
private state state = state.OPEN;
Now, only the state characteristics specified in the State class can be used. So this
solution has become type safe. Since this approach was used very often, it was called
Enum Pattern. One disadvantage is that you can no longer query the state expressions in a
switch statement during processing.
Programmers are often faced with the necessity of representing state expressions in a type-
safe manner. This is what enumerations are for in Java. The compiler translates enums so
that the bytecode corresponds exactly to the enum pattern shown above. Instead of class,
you now write enum. Also, a bit of typing has been taken away from the programmer; the
modifiers static final and the explicit object creation can be omitted; the various options
are separated by commas. You can find this variant in the class GateManager_3.
enum state {
OPEN(“open“), CLOSED(“closed“);
private final String description;
void printState() {
switch (state) {
case OPEN ->
System.out.println(“Gate is open“);
case CLOSED ->
System.out.println(“Gate is closed“);
}
}
Are you missing the default case in this switch statement? Since the checked state is an
enum datatype, we don’t need that anymore as long as all the individual values are also
covered in the cases. That is the case here, and thus the need for the default case is elimi-
nated. Another handy Java customization introduced with Switch Expressions and also
available for Switch Statements.
In the next section, you will change the states of an object.
In the previous paragraph, you saw how you can define state expressions in a type-safe
way. It can now be interesting to see how you transfer one state to another and how the
behavior of an object is changed as a result.
In the GateManager_4 class, the first step is to consider how to transition one state to
another:
Actually, this solution is optimal, isn’t it? When a gate is closed, you open it to transfer
it to the other state. If it is open, you close it and then also have a different state. But with
this coding, it is possible for the user to open a gate that is open or close one that is closed.
And that’s exactly what shouldn’t happen. So query the state expressions beforehand!
Include an if statement in each method that allows opening or closing only if it changes the
object to a different state. If the user tries to open an open gate or close a closed one, alert
them with an error message. You can find this code in the class GateManager_5.
This solution works flawlessly. But where could it show weaknesses? It is no longer
useful when further state expressions are added and there are extensive dependencies
between them.
I would like to extend the example by the fact that your client requests a new condition.
The gate should not only be closed, but also be able to be locked with a lock. You now have
the third state LOCKED. The state LOCKED may only be set if the gate is closed.
Conversely, the LOCKED state can only be changed to the CLOSED state. Figure 8.1
shows the three states and their possible transitions.
You need to relate three state expressions and make the object’s behavior dependent on
them. When a fourth state is added, you can imagine the work that will come your way.
How the method open() could look like in this case is shown in the following listing,
which is only commented out in the sample code:
Similarly, the other four methods would have to define the correct behavior for each
state – even if the behavior is only to issue an appropriate error message. The scope of the
methods increases with each new state. The class becomes unwieldy and difficult to main-
tain. Errors can creep in very quickly. In short: It’s high time for the State Pattern.
In order to be able to exchange the state expressions, they must have a common data type.
You first create the abstract class State, in which all methods from the state diagram are
provided. In addition, this class holds a reference to the goal, which is passed to it in the
constructor. The following code can be found in the StatePattern_1 sample project.
Each state is represented by its own class and extends the abstract class. So, within the
state, you define what should happen when a particular method, for example open(), is
called. When you define a state, you need to think about what a method in that state should
look like. Let’s walk through the example using the Open state. If a gate is open and some-
one tries to open it again, print an error message. Similarly, an open gate cannot be
unlocked or locked; therefore, print error messages in these methods as well. However, if
the user wants to close an open gate, change the state to closed.
@Override
public void open() {
System.out.println(“The gate is already open.“);
}
@Override
public void close() {
System.out.println(“The gate will be closed.“);
gate.setState(new Closed(tor));
}
@Override
public void lock() {
90 8 State
@Override
public void unlock() {
System.out.println(“The gate is not locked.“);
}
}
// … abridged
}
The gate calls the desired method on the current state. However, it has no idea which
state is currently stored. If you call the same method twice on the same object, a different
state may be active, the behavior of the object may be different, and you may have the
impression that you are working with the instance of a different class.
In the client class of the project, the Gate is put to use. Compare the source code, which I
only print here in abbreviated form, with the console output:
GATE.unlock();
GATE.lock();
GATE.close();
GATE.lock();
GATE.open();
GATE.unlock();
GATE.open();
}
I would like to present two options. The first alternative is to manage the state objects
centrally in the context. The second alternative is to create the state objects in the super-
class of all state classes and to pass the correct object to the caller with each method call.
// … abridged
}
The state classes now no longer pass state objects to the context, but instruct the context
to assume a certain state. How it does that is up to it. However, this binds the context very
strongly to the state classes.
// … abridged
}
The final variables are declared protected to allow the subclasses to access them.
@Override
public State close() {
System.out.println(“The gate is closed.“);
return super.CLOSED;
}
// … abridged
}
The context is now responsible for setting the received state object as the current state. This
makes the project very flexible, new state classes can be inserted easily. Because the context
no longer has to provide a set method, incorrect state values are not accidentally passed or set.
You will find the state pattern quite often in practice. For example, network protocols often
work with state expressions. As an example, I have chosen RFC 1939, which describes
POP3. You can find the text of the RFC in the subfolder for the state pattern.
RFC 1939 recognizes three different state specifications: AUTHORIZATION,
TRANSACTION, and UPDATE. These are actually referred to as states in the RFC. The
POP3 server waits for requests. When a request arrives, a TCP connection is established.
The session is initially in the AUTHORIZATION state, where the user’s name and pass-
word are requested. If the name can be identified and the password is correct, the session
enters the TRANSACTION state. In this state, commands can be sent to the server. For
example, a list of stored e-mails can be requested. Furthermore, an e-mail can be marked
for deletion. When the QUIT command is received, the session changes to the UPDATE
state. In this state, the e-mails marked for deletion are actually deleted. Afterwards, the
connection is terminated.
94 8 State
If the QUIT command is issued in the AUTHORIZATION state, the session is termi-
nated without UPDATE. Calling QUIT therefore has two completely different effects
depending on the state. Commands that refer to the stored messages are only permitted in
the TRANSACTION state.
Take a look at the sample POP3 project, which demonstrates the transition of these
three state expressions. I have stored four state classes in the states package: Three for
the states defined in the protocol and one class for the START state, which is needed when
the server is waiting for requests. There is also a class POP3_Session in this package.
This class is the context that the clients access. The client only “sees” the context. It has
no information that a variety of state classes are needed in the background. From this point
of view, analyse the main method of the ApplStart class.
The UML-diagram for State-Pattern from the example project StatePattern_3 can be seen
in Fig. 8.3.
8.6 Summary
Fig. 8.3 UML diagram of the State Pattern (example project StatePattern_3)
The Gang of Four describes the purpose of the pattern “State” as follows:
Allow an object to change its behavior when its internal state changes. It will look like the
object has changed its class.
Command
9
In this chapter, I want to show you a behavior pattern that detaches a command call from
command execution. What is meant by this? Imagine the head of your company saying in
a department head meeting, “I need the latest statistics!” Malice is guaranteed to strike in
the form of your own department head raising his finger and saying, “I’ll take care of it!”
Predictably, he will also assign you to do it, saying, “You do it, you can do it!” What hap-
pened there? The boss requests an action, you execute it, and in between there is an entity
(your department head) that passes on the task. In other words, command invocation and
execution are detached from each other and only loosely coupled via a command object –
and that is exactly the goal of the Command Pattern.
But first, let’s leave the scene in the office and think about how to book a vacation trip. You
go to the travel agency and state your travel wishes. The person in the travel agency types
on his computer for a while and you can go on holiday with peace of mind. But in fact, it’s
not the travel agent who runs the trip, it’s a tour operator who takes care of the flight and,
at the destination, the hotel. Let’s take a closer look at this, but in addition we will also deal
with the Date and Time API added in Java 8, which replaces the old java.util.Date.
In the first step, which has nothing to do with the Command Pattern, I will program a travel
agency and a tour operator. The travel agency defines the method bookTrip(). It is
passed the destination, departure day and return day as arguments. The data is packed into
an object of the class Trip and passed to the tour operator, which then carries out the trip
in real terms, i.e., executes the trip command. You can find the following example in the
sample project Travel_1.
Note the use of the date functions: First, the constructor of the Trip class receives the
start and end dates of the trip as values of type LocalDate. For this, the Trip class must
use the import java.time.LocalDate.
This LocalDate belongs to the Date and Time API, which was implemented in Java 8
with the Java Specification Request JSR-310. The reason for replacing the java.util.
Date used until then were its weaknesses: Lack of type safety, lack of extensibility, unclear
responsibilities (Date with time specification built in but without time zone) and some others.
In contrast, the java.time package and its four subpackages chrono, format,
temporal, and zone provide largely unified commands for various date and time
types, consistent and clear command definitions, and thread-safe immutable objects.
Take another look at the sample code above:
The constructor is about converting a LocalDate into a string. This is done by means of
a DateTimeFormatter, which is provided with the pattern “MM.dd.yyyy” for the repre-
sentation or conversion, which is common for the US. This conversion is not mandatory,
but without it the date would be output according to the ISO standard ISO-8601 in the
format yyyy-MM-dd. Decide for yourself what suits you better.
By the way, a variable of type LocalDate has no time parts at all. It contains the year,
month, and day, as well as all the necessary methods. The counterparts for processing
time information will be discussed separately in a later chapter.
9.1 Encapsulating Commands in Classes 99
The actual conversion into a string is then possible in two ways, both of which I’ve used
to introduce you to them
• Either you use the format method of the LocalDate and specify the DateTimeFormatter
as parameter
• Or you use the format method of the DateTimeFormatter and pass it the LocalDate
as parameter
In both cases, however, the appropriately formatted text comes out. We will see how to
create a LocalDate in a moment when we create the test class.
The tour operators negotiate contracts with hotels, airlines and local bus companies and
run the tour. Each tour operator gets its own company.
In the main method of the test class, a travel agency and a tour operator are created.
Then three trips are booked. Let’s take a closer look at this because of the Date and Time
API. I have highlighted the relevant code passages in bold.
// book a trip
of = LocalDate.of(2023, Month.NOVEMBER, 4);
to = from.withDayOfMonth(15);
travelAgency.bookTrip(“Washington“, from, to);
Note that you need to specify some imports to use the Date and Time API. For this
example, these are.
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import static
java.time.temporal.TemporalAdjusters.nextOrSame;
9.1 Encapsulating Commands in Classes 101
Let’s go over the bolded lines in the test class again in detail.
I create the start date of the first trip using from = LocalDate.of(…). Note that
no new is used in this. The of method creates the object for me (more precisely, a private
method create called by of does that) and returns it. We will learn more about this “fac-
tory” approach in the chapters on the Abstract Factory and the Factory Method. As param-
eters for the call, I use year, month, and day each separately. For the month I use the
standard enumeration Month with the English month names, in this case Month.
NOVEMBER. The first trip therefore starts on 11/4/2023.
However, the customer wants to be back on the 15th of the same month, so I simply
calculate an appropriate end date using from.withDayOfMonth(15). In doing so,
the Date and Time API now creates a new LocalDate with the day changed to the 15th.
For the second and third journey I use the self-written method toDate, which deter-
mines a corresponding LocalDate from a text. For this I use the parse method of the
LocalDate and give it – as already explained in the class Trip – also a DateTimeFormatter.
This can be used in both directions – for parsing as well as for formatting. Since the parser
can throw an exception if it can’t do anything with the text, there must be a corresponding
try-catch block here. For the case of the exception, I return null. You should not be
tempted here to set the current date, for example. This may cause major problems down
the line and is hard to reproduce.
The duration of the second trip is a tricky thing. The client wants to be back on “the
following Tuesday”. Instead of flipping through the calendar myself now, I can elegantly
solve this with the command with(nextOrSame(DayOfWeek.TUESDAY)): Adjust
the start date to “next or same day of week Tuesday” and return it to me as the new date.
For the days of the week, there is also an enumeration that simply saves me from having
to ask which day of the week started the index (was Monday now the 0 or the 1?).
The third trip should last exactly two weeks. This can also be easily solved using
plusWeeks. This command category also includes plusDays, plusMonths and plusYears.
In the Travel class and in the Test class you will find commented out alternative lines of code
for the DateTimeFormatter. Just have a look at the comments in the Travel class on your own.
After this excursion into the possibilities of the Date and Time API, now back to the
actual topic: What you should note in the analysis is that the travel agency calls methods
of the tour operator directly. But that’s about to change!
The travel business is booming – the travel agency has yet to open a branch. I have com-
bined both travel agency and branch under the interface provider. The interface prescribes
the method bookTrip(), which you already know from the last version. The sample
project Travel_2 simulates this situation.
102 9 Command
Otherwise, the code of the classes has not changed. How are the classes used? In the
example code of the test class, you will find the following procedure: You create a tour
operator. You pass the instance to the constructor of a provider. To book a trip, you call the
provider’s method bookTrip() – nothing has changed since the previous version.
Figure 9.1 shows the class diagram of this project version.
However, I used a few more date manipulation options when compiling the travel dates
to show you more possibilities of the Date and Time API.
If you analyze the project Travel_2, you notice that the code of the classes Branch and
TravelAgency is almost identical. Redundant code is always an indication of an inap-
propriate program design. Above all, redundant code is a source of errors that should not
be underestimated. It is therefore necessary to eliminate duplicate code.
How would you proceed if you have many equal factors in mathematics, for example,
5 * 3 + 5 * 2 + 5 * 9? You factor out the same factor in front of the bracket: 5 * (3 + 2 + 9).
You do exactly the same with the redundant program code at the providers.
In the sample project Travel_3, you now pull the same program code in front of the paren-
thesis by introducing the class TripCommand, which lies between the suppliers and the
tour operator. This class solely defines the command that is executed to book a trip. When
it is created, an instance of a tour operator is passed to the constructor and its reference is
stored in a data field. The book() method will be supplied with the same arguments that
were passed to the bookTrip() method in the previous project.
9.1 Encapsulating Commands in Classes 103
The providers – I’m only showing the travel agency here – have now become quite
slim. An instance of the class TripCommand is passed to their constructor, whose refer-
ence is stored in a data field. The method bookTrip() now accesses the instance of the
class TripCommand instead of a travel agent itself.
The changes that have been added in this program version are shown in Fig. 9.2.
But what are the changes in the usage of the classes? In the main method of the test
class, which you can find in the sample project Travel_3, the following procedure is pro-
vided: First, two objects of type TourOperator are created. These objects are passed to
the constructors of two instances of the class TripCommand. Finally, the TripCommand
instances are passed to the constructors of the providers as arguments. It is irrelevant to the
provider which tour operator is working in the background. It only ever calls the book()
method of its TripCommand instance.
What is gained by this approach? At first, it looks like this solution is quite cumber-
some – after all, an additional class is needed. But in fact, several things happen: First, you
have eliminated duplicate code as much as possible. This means you can easily add more
travel providers. You can also pass the same command to different providers. In addition,
the suppliers are now as decoupled from the travel providers as you are from the boss of
your company with his statistics.
Swapping out a command into its own class is the core of the Command Pattern.
104 9 Command
The Command Pattern recognizes several terms, which I will present more formally to
conclude the introduction. There is the caller or invoker; this is the head of the company
requesting a statistic, but this is also a provider selling a trip. The invoker makes use of a
command; that’s an instance of the class TripCommand, for example; but a command
object is also your department head, who passes on the boss’s requests to you. And finally,
there are those who execute the command, these are the receivers – for example, the opera-
tors that actually carry out the trips.
In this chapter I would like to give you two examples that show the use of the Command
Pattern in the Java class library.
You then create an instance of the Thread class and pass an instance of that class to the
constructor:
And finally, you call the start() method on the Thread instance, which results in the
code of a Runnable type class being executed. As an Invoker, the Thread class is as loosely
coupled to the Receiver as travel providers are to tour operators.
threadDemo.start();
While it is obvious, please note that the Runnable does not define any behavior of
the Thread class. The interface Runnable and the prescribed method run() are
only created so that an object can execute a command of a different object, of whose
definition it has no knowledge. If I may formulate the principle casually: The socket
supplies power at a defined interface – whether a lamp, a computer or a washing
machine is operated with it, it is quite indifferent. None of the devices mentioned
defines any behavior of the socket.
You are programming a GUI. On the GUI there should be a button that can be activated by
the user. You pass an ActionListener to the button that contains the code to be exe-
cuted when the button is activated. An object of type ActionListener must override
the actionPerformed() method. When the button is activated, this method is called.
To demonstrate the procedure, first create the button:
Then follows an anonymous class for the code to be executed in the action:
106 9 Command
And finally, pass the Command object, the ActionListener, to this button:
btnExample.addActionListener(actionListener);
The whole thing also works much shorter and clearer with the lambda expressions
added in Java 8, but does the same thing:
Did you notice that this code is structurally quite similar to concurrency example from
before? The logic is indeed the same: you need an object of a certain type to whose previ-
ously defined interface (run() in one case, actionPerformed() in the other) another
object can send messages. Once the invoker has called the defined method of the command
object, the work is done for it. The programmer has defined what should happen when the
Invoker has become active. But the Invoker has no knowledge of how it happens – and
that’s exactly the knowledge it doesn’t need to have, and shouldn’t have.
The classes JButton and JMenuItem have the same superclass AbstractButton.
So, you can reuse the ActionListener from the previous section and add it to the MenuItem
in the main menu of your interface at the same time:
The Action interface enhances the ActionListener interface. You can pass both
Action and ActionListener to a caller as a command. The binding between the
9.3 Reusing Command Objects 107
caller and the command object is much tighter for an object of type Action. Among
other things, the Action interface provides the setEnabled() method, which deter-
mines whether the Action object is enabled or disabled. You can also use an action object
to specify the text and icon of the caller.
Remember what was said about the Template Method Pattern? In that context, I
introduced you to the AbstractListModel class as a template-like implementa-
tion of the ListModel. The AbstractListModel class overrides all methods
for which a default behavior can be reasonably implemented. The methods that are
context-dependent, for example to describe the database, are delegated to subclasses.
Exactly the same thing takes place here. The interface Action specifies the algorithm
and the class AbstractAction partially implements it. The logic of how to
change the state is already implemented; if you change the enabled state of the
Action object, the state of the Invoker is also changed. This behavior should make
sense in most cases; however, you can override it. In any case, the actionPer-
formed() method defies standard behavior, and you must override it.
I would like to demonstrate the reusability of commands with the following example. Take
a look at the Action sample project. In it, two buttons and two menu items are created. The
same action object – a command object – is passed to each button and menu item, i.e. to
two different callers.
First, you create a button and a menu item:
These are added to a GUI. A Command object, an object of type Action, is also cre-
ated. The constructor of the class takes a string containing the display text of the caller.
The only method that needs to be overridden is the actionPerformed() method.
When triggered, it disables the object. The default implementation of the
AbstractAction class is to disable the invokers as well.
Both button and menu item are supplied with the same command object.
btnDisable.setAction(actDisable);
mnDisable.setAction(actDisable);
The text you passed to the constructor is used as the display text by both components.
If you click either the button or the menu item, both button and menu item are disabled.
Figure 9.3 shows you what the GUI looks like.
In this section I describe one last aspect of the Command Pattern. Commands can be
undone and redone. You will find two examples of this. The first example is fairly simple.
The second is a bit more extensive; I will only present the source code in broad strokes – I
want to give you something to tinker with for long winter evenings with this example.
The example project RadioCommand shows how a radio (the older ones among us may
remember) can be operated with the Command Pattern. Besides the frequency adjustment,
which I’ll leave out here, there are four very simple commands: turn on, turn off, turn up
and turn down. All commands implement the interface Command, which specifies two
9.4 Undo and Redo of Commands 109
methods. The execute() method executes the command, the undo() method returns
a command that must be executed to undo its own execute method.
I will explain the command for switching on the radio here as a representative of all
other commands. An object of the type radio is passed to the constructor of the command.
The command is executed on this object. So if the execute() method is called, the com-
mand turns the radio on. If the undo() method is called, the radio is turned off, so the
turn on command returns a turn off command.
The radio must now provide the appropriate methods so that the commands can be
executed.
The radio, in Command Pattern terminology, is the receiver that executes the com-
mands. Invoker is a class Logbook to which the context sends the command call and the
undo. The Invoker logs all command invocations. This allows the context to undo the last
command in each case.
The test class creates an object of the type Radio. In addition, the commands are created
and parameterized with the radio. The commands are then passed to the logbook and executed.
The Swing sample project demonstrates the undo and redo functionality. On the GUI of
the application you will find four buttons. One button is labeled Red and one is labeled
Blue. When you click one of these buttons, red or blue lines are painted on the canvas. The
last command executed is appended to the end of the list on the left side. If you click the
undo button, the last executed command is undone – deleted, that is. However, it is not
actually deleted, but written to the list of commands to be restored on the right. If you click
redo, the command is restored, which is equivalent to calling it again. If you select a com-
mand in the list on the left and click undo, the selected command is deleted. Likewise, if
you highlight a command from the right-hand list and click redo, that exact command is
restored. In Fig. 9.4, you can see what the GUI will look like. I have drawn a few lines and
deleted them again.
The idea for this project goes back to James W. Cooper (Cooper, James William (2000):
Java design patterns. A tutorial. Reading, Mass.: Addison-Wesley. ISBN 0201485397.).
112 9 Command
Fig. 9.5 UML diagram of the command pattern (example project Swing)
I would like to leave the source text essentially to you for your own research. I will there-
fore only present the essential key points to you. The class diagram of the project can then
be found in Fig. 9.5.
When the btnRed button is activated, the execute() method of the CommandRed
class instance is called. This draws a red line on the canvas – the canvas object – and
causes it to redraw itself.
The listener for the btnBlue button looks likewise. Before I go into the listeners for
undo and redo, I would like to take a closer look at the command classes.
In both examples, you have perfect undo and redo functionality. The undo reverses the
execution and the redo corresponds exactly to the original execute command. In reality,
you will also encounter situations where an exact reversal is not possible at all. For exam-
ple, if you bought chocolate in a supermarket and then ate it, you would certainly not be
able to return it. However, it is also conceivable to encounter situations in which an order
can only be partially reversed. Perhaps you bought a book. The retailer takes the book
back, but does not refund your money, but gives you a voucher for it. This only partially
restores the condition as it existed before. You are rid of the book, but you do not have your
money back.
The UML diagram for the command pattern comes from the Swing sample project. You
can find it in Fig. 9.5.
9.6 Summary
Before I summarize the key points of the pattern, I want to draw your attention to one
thing: its similarity to the Chain of Responsibility pattern. Both patterns decouple com-
mand invocation and command execution. In the Chain of Responsibility, the caller sends
its command to a chain of possible recipients; however, it has no way of knowing whether
or how the command will be processed. In the Command Pattern, there is a clearly defined
command executor, the Receiver. It is loosely coupled with the invoker.
Go through the chapter again in key words:
• Commands are outsourced to their own classes: mnemonic: “factor out” the command
in front of the brackets.
• The command pattern decouples invoker and receiver.
• A command class can execute the call itself or delegate it to an executing unit.
9.7 Description of Purpose 115
• Each command class can be parameterized with a different executing unit: for example,
different tour operators.
• The Invoker sends messages to the Command object only; it does not need to know the
Receiver.
• Invoker and command object exchange their messages via defined interfaces.
• A command object can be replaced at runtime.
• A command can be undone.
• An undone command can be restored.
• The Command Pattern allows you to keep a history.
The Gang of Four describes the purpose of the “Command” pattern as follows:
Encapsulate a command as an object. This allows you to parameterize clients with different
requests, queue objects, keep a log, and undo operations.
Strategy
10
The Strategy Pattern is a behavioral pattern. You will always fall back on it when you can
solve a task with different strategies; the term strategy in this context is a synonym for
“algorithm” or for “behavior”. For example, you have a vacation photo and you want to
save it in either jpg format or bmp format. You want to decide at runtime in which format
to save the image. The Strategy Pattern solves the problem for you that you can implement
one and the same task with different algorithms – to save a picture in different formats.
You can easily add new algorithms – strategies.
To get into the subject, you will sort an array. There are very many sorting algorithms that
have their advantages in different areas. In this chapter, I will introduce three algorithms:
the SelectionSort, the MergeSort, and the QuickSort. Let’s start with a very naive approach.
You have a class in which all the algorithms are defined in different methods. You specify
at runtime which algorithm you want to call. Depending on the input, the relevant method
is called. You can find the source code in the example project BadApproach. I didn’t
implement the sorting algorithms themselves; what matters here is the structure of the
application, not the implementation of sorting algorithms.
void choose() {
var question = “How should the data be sorted?“;
Object return = JOptionPane.showInputDialog(null,
question, “selection sort“);
var response = (String) return;
switch (response) {
case “selection sort“ ->
sortWithSelectionSort();
case “merge sort“ -> sortWithMergeSort();
case “quick sort“ -> sortWithQuickSort();
default ->
System.out.println(“Unknown selection“);
}
}
Note that in the switch statement of the choose() method, I use the “arrow syntax”
from the switch expressions we learned about in Sect. 5.7. This eliminates the need for a
break in the switch statement. The code becomes shorter and easier to read. You can
modify this approach by using an if statement instead of the switch statement. However,
you still can’t avoid making case distinctions. So, it’s very messy to implement new algo-
rithms. Also, you have all the algorithms defined in one class. Therefore, the already large
class contains a lot of code that you may never use. You realize that the class source code
is very inflexible; it is every maintenance programmer’s nightmare. So, let’s look at the
Strategy Pattern, which fixes these drawbacks.
The principle of the Strategy Pattern is that you can encapsulate algorithms and make them
interchangeable. How could this be done? Define three classes that define the three sorting
algorithms. If these three classes are of the same data type, the context can interchange
them as desired. Figure 10.1 shows the class diagram of the Sort sample project we will
discuss in this section.
10.2 Strategy in Action – Sorting Algorithms 119
I have provided you with three classes with the sorting algorithms mentioned in the Sort
sample project. They all implement the interface SortStrategy. The interface pre-
scribes the method sort(). The int array to be sorted is passed to this method.
Let us now take a brief look at the logic of the sorting algorithms.
The Selection Sort searches an array element by element and finds the smallest value. This
is written to the beginning of the still unsorted rest array and then this is sorted.
As an example, consider the sequence of numbers below:
In the first step, the zeroth position, i.e., the value 17, is assumed to be the smallest ele-
ment. This zeroth position is compared with every other following position. In doing so,
you notice that there is another position that contains the smallest value in the array,
namely the 3 at the fourth position. The two positions swap their value. So, in the second
step, you have the following array:
The smallest of all values is now at position zero. You continue at position 1. The value
45 is compared with all the following values, and you notice that the 17 at the fourth posi-
tion is the smallest value. So, you swap positions 1 and 4. Now the array looks like this:
Now compare the second position, the value 21, with all the following positions and
swap the 20 for the 21 and so on. You continue until the array is sorted altogether.
The code of the SelectionSortStrategy class encapsulates this algorithm:
Note that this example implementation always brings every smaller number to the
front, not just the smallest. So, there are significantly more swap operations due than really
necessary. There is certainly something that can be optimized. When searching for the
smallest number, you can also first go through the entire field before actually swapping.
This makes it possible to speed up this procedure by more than a factor of 3. I have already
included the necessary adjustments (commented out) in the context. You will see that it is
really only three lines: Re-cloning the number field, assigning the strategy, and executing
it. You are now left with implementing the Selection2SortStrategy class based
on a copy of the SelectionSortStrategy class. You will also find this copy already
as a finished class – but still unchanged from the original. Have fun with this task.
The Merge Sort works on the principle of “divide and conquer”. The array to be sorted is
divided into two parts. Let’s take the following unsorted array:
If you divide this array, you have two parts, the values 17, 45, 21, 99 on the left and 2,
20, 15, 12 on the right. If you divide these two halves again, you have the following
four parts:
Obviously, it doesn’t make sense to keep dividing the individual lists, so now sort them
individually:
Now the sorted sublists are merged in pairs, making sure that the new lists are sorted
correctly:
And these lists are now sorted and put together again to the final result.
The algorithm for this can be found in the MergeSortStrategy class.
The Quick Sort also works on the divide and conquer principle. Next to us the unsorted
array from above:
10.2 Strategy in Action – Sorting Algorithms 121
This array is again divided into two parts. You calculate a value, a pivot element, so that
about half of the values are smaller and the other half are larger than this value. The
smaller values are written to the left list, and the larger values are written to the right list.
For demonstration purposes, I’ll take the value 40 as the pivot value here, so you have
two lists:
“Purely by chance”, the right-hand list contains only two values – so there’s no point in
dividing it again; sort it and you’re done. However, consider the list on the left. You could
take 18 as the pivot value. Put all values less than 18 on the left, and the others on the right.
So you have:
The right list contains “purely by chance” again only two elements, which you sort. You
may then merge them with the already sorted values:
You divide the “left list” again according to a given pivot element and so on.
Both Merge Sort and Quick Sort work recursively. Quick Sort is fast if you manage
to compute a pivot value at each step such that there’s roughly an equal amount of
numbers in each of the left and right lists. In my implementation of the
QuickSortStrategy class, I simply pick the first element of the list. There is a
lot of literature about sorting algorithms; so much that I don’t want to give a prefer-
ence here. My descriptions here are only very superficial, but also not really relevant
to the topic of design patterns per se.
All sorting algorithms are of type SortStrategy. So you can rely on them to define the
sort() method that triggers the sorting process. You declare a variable within the con-
text that holds a reference to the desired strategy. An assignment then specifies the strategy
to use. When you want to sort the array, you simply call the strategy’s sort method. A snip-
pet from the Context test class:
executeStrategy(sortStrategy, number_1);
sortStrategy = new MergeSortStrategy();
executeStrategy(sortStrategy, numbers_2);
sortStrategy = new QuickSortStrategy();
executeStrategy(sortStrategy, numbers_3);
}
In Sect. 9.1 we took a first look at the Date and Time API and looked at the LocalDate.
Remember the note about it not containing any information about times? In the code
above, you can see the counterpart to that: The Instant class. This class contains only
time information and appropriate methods for calculations accordingly. You can see a
simple application here: A look at the starting time of the sort. Then a look at the clock
when everything has run, followed by the calculation of the duration (as a separate class
Duration for the length of time intervals) and its output in milliseconds.
By the way, this benchmarking approach is very naive and only meaningful at first
glance. It is enough for us here to make a rough comparison of the different algo-
rithms. But the startup times of the Java Virtual Machine (and also under different
environmental conditions) are not taken into account here. If you have serious per-
formance measurements of Java programs in mind, you should look into the “Java
Microbenchmarking Harness” (JMH) added in Java 12 with the JEP 230. However,
a description of this small collection of tools goes too far here and misses the actual
topic of the book.
10.3 The Strategy Pattern in Practice 123
What do you think of this approach? Surely one advantage jumps right out at you: The
context is much clearer! But there are two more advantages:
The project can be extended to include any number of sorting algorithms. If you want
to implement the Heap Sort, define a new class that implements the SortStrategy
interface. The context can use your HeapSortStrategy immediately. For the Selection
Sort, I suggested an enhancement option. You can also implement that as a variant of a
SortStrategy and compare it directly against the Selection Sort I created.
In principle, the context does not even need to know how his problem is solved. But
why only “in principle”? You offer a variety of algorithms that all solve the same problem.
In order for a programmer to know when to choose which algorithm, you need to docu-
ment very precisely under which conditions which algorithm is appropriate. In doing so,
you will not be able to avoid going into implementation details in the documentation.
In the implementation above, you as the user have specified which strategy you want to
use. However, it would also be conceivable that the program asks the strategies how well
they can solve a certain task under certain conditions. An algorithm that sorts data in main
memory very efficiently might fail if the data is stored in a file and cannot be fully loaded
into main memory. So the context could ask the strategy classes, “How well do you solve
the task under the condition that the data is stored on disk?” Each strategy class would
return a score, say in the range zero to 100, and the context can then select the strategy that
returns the highest score.
Another advantage of the Strategy Pattern is the reusability of the algorithms. Imagine
you are programming an office suite. Within the spreadsheet, you need to be able to sort
data. But you also want to sort data in word processing. After all, the algorithms are the
same, so you can reuse them and, in the case of the spreadsheet, limit yourself to the func-
tionality that is typical of the spreadsheet. For word processing, you program only the
parts that are typical for word processing. You can reuse the sort algorithm.
In the example above, I have assumed that the data to be sorted is passed to the sort()
method. This specification certainly makes sense in this constellation. However, think of a
family of algorithms where one implementation takes a lot of parameters, but another
implementation takes significantly fewer. Because of the common interface, the context
would still have to pass all arguments – an avoidable overhead.
Where can the Strategy Pattern be found in the Java class library? In the area of GUI pro-
gramming, you will find the Strategy Pattern in several places. For example, there are
LayoutManagers or the “Look and Feel”; containers are provided with a certain strategy
by default, which can, however, be exchanged by the user.
124 10 Strategy
Instances of the JPanel class are needed to combine components of a GUI. By default,
the FlowLayout is used for this. You can understand this, for example, by executing the
following code once in the Java shell:
javax.swing.JPanel pnlLayoutTest =
new javax.swing.Jpanel();
java.awt.LayoutManager layout =
pnlLayoutTest.getLayout();
System.out.println(layout);
Fig. 10.3 UML diagram of the Strategy Pattern. (Example project Sort)
From the Sort example project, you can see the UML diagram in Fig. 10.3.
You know the Command Pattern and the State Pattern, and in this chapter, you learned
about the Strategy Pattern. All three patterns encapsulate behavior. In fact, the class dia-
grams of State and Strategy look pretty much the same. You can keep track of them by
visualizing the different goals:
The Command Pattern is where you encapsulate commands. You need it to pass differ-
ent action listeners to all buttons on your GUI. One command opens a file, another saves
it. The Command Pattern does not describe the behavior of the calling object.
The Strategy Pattern encapsulates algorithms. Behavior of an object is implemented in
different ways: There are many algorithms to compress files, to encrypt or to sort data. But
you will only ever need one algorithm. So, out of the multitude of strategies, choose the
one that solves your task most efficiently.
You use the state pattern to encapsulate state characteristics. The behavior of an object
is based on its state. When an object has a certain state, it exhibits different behavior than
when it is in a different state. Think of the open gate – the gate can be closed, but not
locked. Only a closed gate can be locked.
126 10 Strategy
10.6 Summary
• The Strategy Pattern is used when you have multiple algorithms for a task.
• Each algorithm is defined in its own class.
• All Strategy classes implement the same interface.
• The context is programmed against the interface.
• At runtime, an algorithm is passed to the context.
• The context calls the solution strategy, the algorithm, without knowing which algo-
rithm is behind it.
• The algorithm can be reused.
The Gang of Four describes the purpose of the “Strategy” pattern as follows:
Define a family of algorithms, encapsulate each one, and make them interchangeable. The
strategy pattern allows the algorithm to vary independently of clients using it.
Iterator
11
Java knows different collections, for example the classes ArrayList and LinkedList. The
data is stored differently internally in these classes. However, it is of interest for the client
to be able to iterate over the data without knowing the internal structure. In this chapter,
you will look at collections in detail and recreate the ArrayList and LinkedList classes.
You will also create an interface that allows you to iterate over these classes.
In the following sections, I will show you two ways to create collections.
The first collection you encountered at some point was most likely the array. In an array,
you store any number of elements of a particular data type. With the declaration int[]
numbers = new int[5], you create an array that can store five int numbers. You
access the individual memory areas very efficiently. An array has the disadvantage that it
cannot be enlarged. This is impractical if it turns out that more data is to be stored than it
was originally intended.
The basis for our first collection will be an array. The elements in this array will be of
the general type Object. Initially, five objects are to be referenced. To be able to store dif-
ferent data types in a type-safe way, create the class generically. In the sample project
Collections_1 you will find the following code:
To insert a new element, define the add() method. It is passed an argument of generic
type. This element is stored at the next free position in the array. The counter data field
stores this position. If five elements are already stored and a sixth is to be added, the data-
base must be expanded. Since an array cannot be enlarged, the only way left is to redefine
the array. In general: If the next free position is equal to the number of elements, the size
of the array must be increased by a certain value. In the example, the array size is to be
increased by another five elements.
The client may want to inquire how many items are stored in the collection. For this, it
is sufficient that you return the position of the next free item.
The collection only fulfills its purpose when the individual elements can be returned.
To do this, you create the get() method, which expects an index as a parameter that
describes the location of the element you are looking for in the database. Before the return,
the stored value is cast to the generic type.
If you want to delete an element, the counter must first be decremented. The element is
then deleted by moving the subsequent elements forward one place at a time. However, in
order not to throw an “index out of bound” exception, checks on the range between 0 and
counter are still required. And to avoid leaving the removed object at the end of the field
in memory, it must be overwritten with null.
11.1 Two Ways to Store Data 129
By the way, the collection you have just developed corresponds in its methodology to
the ArrayList of the class library. It is optimal if you need to access individual elements via
their index.
You take a completely different approach if you store the individual elements not in an
array, but in a chain. Each element knows its successor. It would also be conceivable that
an element also knows a predecessor; I won’t go into this possibility further – the project
would only become unnecessarily extensive without changing the underlying principle.
Strings and any other objects you want to store in your list, I call elements. They are not
stored in the collection, but in the instance of an inner class I call node. The Node class
has two data fields that store the element you want to store and the subsequent node object.
The collection can then restrict itself to referencing the first object (in the header
data field).
Data is inserted into the collection by creating a new node object. This object is refer-
enced by the header variable and displaces the object previously stored as header.
The nextNode field of the new header object references the previous header. And
130 11 Iterator
finally, the counter must be incremented. When you query the size of the collection, the
counter is returned.
@SuppressWarnings(“unchecked“)
public void add(E element) {
header = new Node(element, header);
counter++;
}
To delete an element from the collection, go through the collection with a while loop
and check whether the referenced element is the same as the element you are looking for.
If so, pass the reference of the subsequent node object to the predecessor of the node
object that references the element you are looking for. Then decrement the counter. The
local variable previous references the predecessor of the node object whose element is
currently being checked.
The get() method is intended to solve the same task as the get method of the
MyArray class. However, because the database is not index-based, you cannot directly
query the xth element in the collection. You must go through the entire collection until you
find the xth element.
11.2 The Task of an Iterator 131
By the way, the collection you just developed is similar in methodology to the LinkedList
of the class library.
When you create a collection, you will certainly want to iterate over all elements. A first
approach could be the procedure of the test methods (of the respective main method),
which you can find for both classes in the sample project Collections_1. Please analyze
them and run them. In both test methods, iterate over the data collection with a for loop.
You access each element of the collection with the get() method. With the MyArray
class, this makes perfect sense. However, the performance of the MyList class falls far
short of its capabilities when accessing its elements in an index-based manner. So it makes
sense to outsource the algorithm of how to iterate over the collection to its own class, the
iterator. You can think of the iterator as a bookmark that is pushed through a book page by
page. The iterator knows the specific features of a collection and makes the best use
of them.
The class library knows the interface Iterator, which is an interface for all conceivable
iterators. With hasNext() you let yourself return whether there are still further elements
in the data collection. The next() method returns the next element in the collection. If
the client wants to access an element that doesn’t exist, throw a
NoSuchElementException. And finally, remove() deletes the current element
from the underlying data collection. According to the specification, the remove()
method does not need to be implemented, it is allowed to throw an
UnsupportedOperation Exception.
The simplest form of an iterator is returned by the iterator() method in the MyArray
class. The iterator internally stores the position at which the bookmark is set. The next()
method returns the next element in each case, and the hasNext() method returns true
if there are more elements. Take a look at the sample project Collections_2 and there the
class MyArray.
@Override
public boolean hasNext() {
return (position < size())
&& elements[position + 1] != null;
}
@Override
public E next() {
position++;
if (position >= size()
|| elements[position] == null)
throw new NoSuchElementException(″No more data“);
@SuppressWarnings(″unchecked“)
var value = (E) elements[position];
return value;
}
11.3 The Interface Iterator in Java 133
@Override
public void remove() {
throw new
UnsupportedOperationException();
}
};
}
// … abridged
}
After that, you have an iterator returned and query data in a while loop until there is no
more data in the collection. To provoke the exception, you specifically retrieve one more
element than is stored.
Each string is now output on the console. Afterwards the exception is thrown.
Alternatively, you could have an iterator that first copies and/or sorts the database before
returning the individual elements. Also, if you build your own complex structures (tree
structures, …), you can design a “standard” iterator for it according to your needs. Of
course, you don’t have to define the iterator as an anonymous class – you could also design
a class outside the collection’s namespace.
The class MyList - also in the project Collections_2 – is internally structured differently.
The individual elements are not stored in an array, but linked. What consequence does this
have for the iterator? In contrast to MyArray no counter is stored as bookmark, but the
current element. Initially the current element is set to the header of the list.
If you want to test whether a collection contains more elements, you must ask whether
the current element of the iterator is non-null, in which case the hasNext() method may
return true. The next() method returns the contents of the current element and
advances the pointer to the next element one place.
@Override
public E next() {
if (current == null)
throw new NoSuchElementException(“...”);
@SuppressWarnings(“unchecked“)
var value = (E) current.element;
current = current.nextNode;
return value;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
Again, at first all strings are printed to the console; then an exception is thrown.
A while loop is related to the for loop. Instead of the while loop.
while (iterator.hasNext()) {
// … Action
}
Since Java 5, there is the for-each loop. Iterating becomes much easier:
How can you prepare your MyList and MyArray collections so that you can use
them in a for-each loop? All you need to do is implement the Iterable interface. This
interface dictates the iterator() method, whose return value is the collection’s itera-
tor. You can see this in the Collections_3 sample project – here using the MyArray class
as an example:
Now you can use the MyArray class in an extended for loop:
This works accordingly for the MyList. Please have a look at it again independently.
Search the API documentation for the interface Iterable. A subinterface of this is
the Collection interface. This interface is implemented by all common collections
such as List and Set and by many others. Therefore, you can trust that all collections
define the iterator() method, which returns an iterator object.
With a map, the issue is a bit more complicated. As an example, consider a HashMap
that consists of three collections: a KeySet for the keys, a Collection for the values,
and an EntrySet for the connection between the two collections. No one can know in
advance whether the user will want to iterate over the keys or the values; therefore, the
HashMap cannot design a “default” iterator.
The for-each loop is a very useful language construct for the programmer. However,
although a lot of work is done for the programmer, the compiler has relatively little work
to do with it. It rewrites the for-each loop into a while loop before compiling it (code
rewriting) and lets you give it the iterator. If you iterate over an array with the for-each
loop, the loop is rewritten into a conventional for-next loop and then compiled.
You can see from this example how patterns have found their way into the class
library. Not only the name, but also the realization of the pattern corresponds to the
description of the GoF.
11.6 Summary 137
From the example project Collections_3/MyList you can see the UML diagram in
Fig. 11.1.
11.6 Summary
Fig. 11.1 UML diagram of the Iterator Pattern (sample project Collections_3/MyList)
138 11 Iterator
The Gang of Four describes the purpose of the “Iterator” pattern as follows:
Provides a way to sequentially access the elements of a composite object without exposing
the underlying representation.
Composite
12
The Composite Pattern, which I will now introduce, belongs to the Structural Patterns. It
describes how objects are put together to form larger meaningful units. A composite struc-
ture is great for displaying in a JTree. So, in this chapter I will also describe how to define
your own data models for a JTree.
You have a structure that consists of several units. These units can optionally contain
smaller units. You want to be able to call certain methods on all of these units without hav-
ing to worry about whether or not the unit contains other units. What is meant by this?
If you keep a budget book, write down the individual items of your income and
expenses. The income includes your salary from your main job. Maybe you also have a
side job, then this is also written under the heading of income. To the expenses belongs
certainly your rent or rates for an own real estate. In addition, you also have items that can
be subdivided. Under the heading of food, there are various sub-categories: Lunch, Eating
out with friends, etc. In these subcategories you have the individual items like “Pizzeria
16.00 €” or “Canteen 4.00 €”. You can ask at the lowest level: “How much did the visit to
the pizzeria cost?” But you can also add up by asking, “What did I pay in total for food?”
And finally, at the top level, you can ask: “What did I take in and spend in total?”
Or think of a company that consists of different departments that are divided into sev-
eral hierarchical levels; if you ask an employee there about the personnel costs, he will
name his own salary. If you ask a department manager for his personnel costs, he will first
determine the personnel costs of his subordinate employees, add his own salary and give
you the total sum back. If you ask the owner of the company, he will return the total per-
sonnel costs of all departments in addition to his own.
Very practical: The file system of your computer returns the size of a folder as well as
of a single file.
In short, the Composite Pattern helps you represent tree structures. Let’s clarify some
terms! Each item in a tree structure is a node. A node that has no child nodes is a leaf; for
example, this is the single item “Pizzeria 16.00 €”. A node that has subnodes is called a
composite; this is, for example, the item “food”. The node at the top of the tree (the com-
pany owner) is the root.
There are two different approaches to implement the composite pattern.
Leafs and composites must have different behavior; consequently, they must be defined in
different classes. However, composites can store both other composites and leafs as nodes;
therefore, composites and leafs must have a common interface.
In the sample project Budget_1 you will find a first draft. The class Node is the com-
mon interface for leaves and composites. It defines a data field that describes the revenue
or spending item; this field is needed for both leaves and composites. And finally, the
print() method is prescribed.
The class Leaf extends the class Node. It additionally defines a field in which it is stored
whether the position is required or whether it is luxury. For revenue, this flag certainly
doesn’t matter. However, you will certainly think about whether an expense was necessary
or not. It also defines a data field that stores the amount of the item. The print method takes
an integer value that describes the number of indentations; tabs are inserted according to
this number before the value of the item is printed. Items that are required are preceded by
an exclamation mark; outputs that are not strictly required are not marked separately.
System.out.print(“\t“);
System.out.println(this);
}
@Override
public String toString() {
var prefix = required ? “(!) “ : “( ) “;
var tempAmount =
NumberFormat.getCurrencyInstance().format(amount);
return prefix + description + “: “ + tempAmount;
}
}
The Composite class defines a list in which the child nodes are stored and the required
access methods. This includes, for example, a method that returns the number of child
nodes and a method that returns the child at a specified position. The print method is over-
ridden in such a way that, according to the indentation, first the value of the toString
method is output and then recursively all child nodes.
@Override
public void print(int indentation) {
for (var i = 0; i < indentation; i++)
System.out.print(“\t“);
System.out.println(this);
children.forEach((node) -> {
node.print(indentation + 1);
});
}
@Override
public String toString() {
return description;
}
}
142 12 Composite
The client creates variables for the root and various expense categories, for example,
income and expenses. Among the expenses, there is the category books, where two books
are set. I am only printing the listing in abbreviated form.
The client can now restrict itself to calling root.print(0). Then the income and
expenses for January are clearly output to the console:
Budget book
January
Income
(!) Main job: 1.900,00 €
(!) Side job: 200,00 €
Expenses
Insurances
(!) Car: -50,00 €
(!) ADB: -100,00 €
Books
(!) Design Patterns: -29,90 €
( ) Trashy novel: -9,99 €
(!) Rent: -600,00 €
It is possible to have only the January expenses printed; to do this, call the print com-
mand on the expenses composite: expenses.print(0). This solution works per-
fectly. However, it proves to be too inflexible in certain places – and you will see and
correct this in the following section.
12.3 Implementation 2: Transparency 143
The client code violates the principle that one should program against interfaces and not
against implementations: final composite root = new composite(“Budget
book“). This was unavoidable because the interface node declares only those methods
that are actually needed by both leaves and composites. This “lowest common denomina-
tor” does not include add(). And this is where a huge problem starts to mature – the
program from the previous approach could only be made to work with a little “trick”. The
print method calls itself recursively on all objects. However, if you wanted to access the
list with an external iterator or have a single item returned, you would have to work with
numerous comparisons (instanceof composites) and downcasts. However, down-
casts are the programmer’s crowbar; if possible, they should be avoided.
To demonstrate the problem, I want to print the list to the console again, but this time
using an external iterator. I delete the print method in the sample project Budget_2. Now
the client itself has to take care of the iteration. So, the most important change in this proj-
ect is in the test routine area. First, create some income and expenses as in the previous
example. Then, within the test class, call a newly defined print method. In this method,
first the required tabs are printed and then the object itself. It then checks whether the item
to be printed is a composite. If so, the node object is cast to a composite object. And finally,
ask the composite for the number of its child nodes and output each child node to the
console.
Do you think the example is a little contrived? Not at all! You’re about to see an imple-
mentation of the TreeModel interface – where it would be difficult to implement with an
internal driver.
Do you notice anything about the instanceof command? Here I used a preview feature
newly added in Java 14: Pattern Matching for instanceof, found under the Java Enhancement
Proposal JEP 305. You can only use it if you enable preview features in Java 14 or 15 (JEP
375). This is done by the additional compiler option –enable-preview, which you have to
add in the compiling project settings of NetBeans or just in the command line call of the
Java compiler javac. In Java 16 (JEP 394) this feature is final. But what does this pattern
matching actually do?
We get the reference to the node, which can be either a leaf or a composite. If we want
to act differently depending on the subclass, we check with instanceof that it belongs to the
class Composite and then cast the reference “down” to that class. Previously, in Java, this
was two steps: first the check, then the casting. Now it works in a single step. You can see
in the code that after the previously used if (node instanceof Composite),
there is now another composite. This automatically introduces this variable and casts
the node to the class composite. The additional line Composite compos-
ite = (Composite) node; is thus omitted completely.
Such checks are used very frequently in Java. Pattern matching simplifies them in both
programming effort and readability.
The Budget_3 sample project shows how you establish generality. You define the fol-
lowing two methods in the class Node:
The class Composite overrides these methods as before. What is new in this realization
is that a leaf can now also specify how many children it has – namely none. If a client
nevertheless tries to call a child at a specific index, a RuntimeException flies up in its face.
12.4 Consideration of the Two Approaches 145
So the client has to make sure that the node to be printed actually has child nodes. The
print method in the test class is formulated more generally.
A leaf object returns 0 as the number of children. The condition of the for loop is there-
fore not fulfilled, a recursive call of the print method does not take place.
The first approach relies on a narrow interface. A leaf can only do what it absolutely must
be able to do. Therefore, you will find management methods for the list of child nodes only
for composites. Security is the primary concern with this approach. The client has to com-
pare and cast, but it has the guarantee that the methods it calls are executed in a way that
makes sense. The AWT, for example, is based on this principle. There is the abstract class
Component. In it, methods are defined that use both leaves and composites: Register
listeners, change visibility, etc. The various controls (Button, Label, CheckBox,
etc.) are derived from Component. In addition, the Container class inherits from
Component. It defines methods to manage components as child nodes. Only those
classes inherit from Container that must be able to accommodate other components, i.e.
Frame and Panel.
The alternative is to use a broad interface: leaves and composites can in principle do
anything. Swing is built on this approach. There is the class JComponent, which inherits
from Container. All controls inherit from JComponent, including JLabel, JButton,
etc. So they all have the method add(), to which you can pass a Component object.
Subsequently, it is possible to store a JPanel in a JLabel, which you can check with
the following code, e.g. in the Java Shell of NetBeans.
Fig. 12.1 Class diagram AWT and Swing components, borrowed from www.PhilippHauer.de
12.5 Going One Step Further 147
The interaction of AWT and Swing is shown in Fig. 12.1. I borrowed the diagram from
Philipp Hauer. Philipp deals with design patterns, among other things, on his site www.
PhilippHauer.de. It is definitely worth visiting his site.
An interface that is too broad is problematic. Take project Budget_3 as a basis. There,
you define methods that are specific to a composite in the interface in order to obtain gen-
erality. In the test class, you query the number of child nodes and have a child node
returned. I’m sure that’s not a problem. But it gets tricky when you need to include the
addChild() method in the interface. How does the Leaf class implement this method?
What does the default implementation look like? One solution might be to have the leaf
simply do nothing add(){}. But an empty implementation is not without its problems –
the client will certainly want to know that its job cannot be executed. So the client can’t
avoid making a case distinction up front. Although this approach can be problematic, it is
favored in practice, and the GoF also advocates it.
It would be a useful thing if the categories could sum up the individual child items, and this
is realized with the example project Budget_4. To do this, you create a cache in the com-
posites that stores the sum of the child nodes. In the interface, you declare an abstract
method calculateCache(), which must be overridden by leaves in the same way as
by composites. Also, both leaves and composites should be able to return the internally
stored value.
The definition in the Leaf class is quite simple – a leaf does not need to create a cache,
so the method can be overridden empty. However, a leaf must be able to name the
amount stored:
@Override
public void calculateCache() {
}
@Override
double getValue() {
return amount;
}
}
The composite class returns the cache when the getValue() method is called. The
calculateCache() method is defined so that the cache is first cleared. Then, all chil-
dren are recursively instructed to calculate their cache. Finally, the stored amount is que-
ried from all children and added to their own cache.
@Override
public void calculateCache() {
cache = 0;
for (var node : children) {
node.calculateCache();
cache += node.getValue();
}
}
@Override
double getValue() {
return cache;
}
@Override
public String toString() {
var tempCache =
12.5 Going One Step Further 149
NumberFormat.getCurrencyInstance().format(cache);
return description + “ (Sum: ” + tempCache + “)“;
}
}
The test of the program differs only minimally from the previous examples. The tree is
constructed as before. Then the root node receives the instruction to calculate its cache.
When you start the program, you get the subtotals of the categories.
Since the project is very manageable, it would certainly have been reasonable for the
categories to recalculate the cache each time getValue() is called. However, a
cache makes sense if the call of getValue() in the leaf causes high costs.
In the following step, the structure is created as a doubly linked list – the parents know
their children, but the children also know their parents. You probably know this from
150 12 Composite
Swing – you call the method getParent() on a component; the parent node is returned.
In the Budget_5 example project, you maintain new income or expenses; the parent cate-
gory is notified and can update its cache. In the Node class, add the Composite parent
field with the appropriate access methods.
The add() method of the Composite class is extended. A composite passes itself as
parent to each newly added child node. If the new child to be added already has a parent,
an exception is thrown – because the situation should not occur.
The introduction of a reference to the parent node does not yet have an effect in this
project version. The parent node is used in the sample project Budget_6 to recalculate the
cache. If you insert a new leaf or change the value of a revenue or expense item, all
affected composites pass this information on to the parent until the message reaches the
root. The root node then recursively calls the calculateCache() method on all child
nodes, as in the previous project.
12.5 Going One Step Further 151
You want only the composites to recalculate their caches that are affected. You intro-
duce a flag in the Composite class that indicates whether the cache needs to be recalcu-
lated. When a new node is added to a composite, the cache is no longer valid. So add()
causes its own cache and the parent node’s cache to be recomputed. The same is true when
a node is removed. If the calculateCache() method just recalculated the cache, it is
certainly correct and the flag may be set to true. The newly added setCacheIs-
Valid() method is interesting. If true is passed to it, the flag is corrected. If false is
passed to it, the parent node is additionally instructed to mark the cache as invalid. If there
is no parent – this only applies to the root node – the child nodes are instructed to recalcu-
late their cache. However, these only recalculate the cache if the flag isValid is set
to false.
@Override
public void calculateCache() {
if (!cacheIsValid) {
cache = 0;
for (var node : children) {
node.calculateCache();
cache += node.getValue();
}
this.setCacheIsValid(true);
}
}
}
I take the same test routine as in the previous project. I can do it without root.cal-
culateCache() now, because the structure calculates the intermediate values “auto-
matically” when new positions are entered. To test the new functions in more detail, I add
a retirement provision to the insurances, which costs 1000.00 €. I have the budget book
output again and find that the figures are correct. I notice that the retirement provision is
not monthly with 1000.00 €, but annually. So, I delete the insurance and have the budget
book displayed again – the figures are correct again. I insert the retirement provision again,
but this time at the same level as the months, i.e., directly below the root. And again, the
correct values are output. The structure of the pattern and the calculation method allow a
very easy to use automatic update.
Did you think it was a bit awkward how I moved the retirement provisions? Me too –
let’s correct that in the next section.
The sample project Budget_7 deals with the question of how to move a node to another
node. Since both leaves and composites must be moved, the method changeParent()
12.5 Going One Step Further 153
Fig. 12.2 UML diagram of the composite pattern. (Example project Budget_7)
is defined in the class Node, to which you pass a reference to the new parent node. The
method returns the current parent and deletes itself there as the child node. With the new
parent, the node enters itself as a child node. Since both add() and remove() cause the
caches of the composites to be recalculated, the entire tree structure is up-to-date and cor-
rectly calculated again.
From the example project Budget_7 you will find the UML diagram in Fig. 12.2.
12.7 Summary
The Gang of Four describes the purpose of the “Composite” pattern as follows:
Compose objects into tree structures to represent part-whole hierarchies. The composition
pattern allows clients to treat both individual objects and compositions of objects
consistently.
Flyweight
13
The Flyweight Pattern is a structural pattern; in order to understand it, the terms intrinsic
and extrinsic state must be defined. Therefore, I will present two examples in this chapter.
The first example clarifies the principle of the pattern, the second is closer to the example
of the GoF and describes these two terms.
The flyweight has the task of turning an elephant into a mosquito. Imagine programming
an application that records all births in a year. Each record consists of an ID1, first name,
last name, and birthday. Take a look at the very simple example project Birthrates.
According to the Federal Statistical Office, just under 780,000 children were born in
Germany in 2019. If we assume that the births are evenly distributed over the year, then
over 2100 people have their birthday on the same day. According to the Gesellschaft für
deutsche Sprache e.V. (Society for the German Language), about 2.52% of newborn girls
had the first name Marie in 2019, which would therefore result in the same new first name
being given about 54 times per day. And surname sameness is also certainly likely for
newborns on a single day (In Germany, some common family names are Müller, Schmidt,
Schneider, Fischer, …).
So the above approach would result in you having over 2100 identical Date objects;
also, the previous programming would create thousands of strings with the same first name
and same last name. And we’re only talking about one birth cohort here. For the entire
population in Germany, you would have to manage over 83 million first and last names
individually.
In the simple example code shown above, the compiler is intelligent enough to detect
this and optimize it, but if the names need to be read from external sources, the compiler
cannot detect this in advance. It would not take long for the program to blow up the avail-
able memory.
The Flyweight Pattern now describes how objects of the smallest granularity can be
reused. Let’s look at what is meant by this in the next section.
13.2 The Realization 157
Take again as a negative example the project births. Let’s assume for the sake of simplicity
that the majority of children get either fashionable names or traditional names. The num-
ber of different first names will be very small in proportion to the 780,000 children. I’m
assuming there are 200 different first names given in 2019. It would make sense to not
create 780,000 string objects that are almost all the same anyway. It would make more
sense to create 200 different string objects. Children with the same first name can share a
string object.
The example project Birthrates_Flyweight demonstrates this. First, it is noticeable that
the child objects are not called or created directly by the client. The client calls the method
getChild() on a factory and passes the data of the child to this method.
The factory keeps a HashSet in which all first names are stored. When a child is to be
created, the call to the add method adds the name to the HashSet only if it is not already
contained. Surnames and birthdays are handled in the same way.
surnameSet.add(surname);
dobSet.add(dob);
return new Child(forename, surname, dob);
}
}
Nothing changes in the child class itself. The check to see if an entry already exists in
the set is handled here by the add method of the HashSet, so we don’t have to worry about
that at all. This makes the code very simple.
The advantage of this approach is that you have reduced a large number of identical
objects by dividing the objects.
When objects are shared, you need to critically examine whether you define them muta-
ble or better immutable. If a child is renamed from Peter to Paul, the change of name
would affect all births that refer to it. However, since strings are immutable, this problem
is unnecessary here. With birthdays, the situation would be more problematic; a Date
object could be set to a different value with date.setTime(). Thus, all child objects
referencing the date would suddenly have a different birthday.
This approach is particularly interesting when these split objects are either very large
and/or costly to create, for example by querying a database.
This example is extremely simple. In the following section, you will dive a little deeper
into the matter.
Now let’s look at an example that is a bit more complex. You program a software that takes
orders for a pizzeria.
Your first approach might be to define a Pizza Order class that stores the name of the pizza
and the table to which it should be delivered. Take a look at the Pizza sample project.
The Client creates new order objects and stores them in a list. When each table has
placed an order, the pizzas are served.
If you analyze the code, you will see that each pizza is ordered multiple times. Of
course, in individual cases, it is also possible that two or more orders are placed at one
table. Which attribute will you share – the pizza or the table number? It takes a lot of effort
to make a pizza. So, since your goal is to save resources in your ordering software, you
will share the pizza – but note: only in the software, of course, each guest will end up with
the whole pizza of their choice. The number of the table the order comes from will not be
shared – the client will be blamed for that right away. So you have two different states: the
state that is shared; and the state that the client is held responsible for. You call the shared
state intrinsic; the other extrinsic. Objects that define the intrinsic state are the fly-
weights. They are independent of the context in which they are used.
The PizzaOrder class has two attributes: the description of the pizza and the number of the
table to which it should be delivered. You decompose the PizzaOrder class into an intrinsic
state and an extrinsic state, where the description of the pizza itself should be the intrinsic
state. So, you pull the other attribute out of the class and move it into the context. What
160 13 Flyweight
remains in the pizza class is only what is necessary to describe the pizza. This is not the
baked pizza itself, but the note that it was ordered. In the example project PizzaFlyweight
it should be sufficient to define the pizza by its name.
// … abridged
}
To allow for more dishes (salad, pasta, …) later on, I introduced the interface
MenuEntry right away in this context, which is implemented by the class Pizza. I’ll
go into this in more detail in a moment; let’s look at the client code first.
The client manages the different pizza objects and the table numbers. To enable a
unique assignment, the orders are stored in a map; the key of this map is the table number,
the value is the ordered menus. In order to represent the situation that a table can place
several orders, the menus are stored as arrays.
The code of the factory, which keeps an overview of all ordered variants, is almost
identical to the code you already know from the example project “Births”. I do not want to
go into it any further.
How is the pizza served to the table? The interface menu prescribes the method
serve(), to which you pass the table as a parameter. The GoF describes this by saying,
“If operations depend on extrinsic state, they get it as a parameter.”
Now let’s introduce the rest of the pizza class, the serve() method. It can be limited
to outputting the message on the console that the pizza has been served to a specific table.
Once the client has taken all the orders, he goes through the orders table by table and
serves the pizzas.
In the following paragraph you can see where the flyweight is used in practice.
162 13 Flyweight
Where can you find the flyweight pattern in the wild? When you create an integer object
using the new operator, you compare the objects’ references for equality using ==. Run the
following code in a Java shell:
If you run this code, you will see that all numbers from −128 to +127 are divided:
What happens when you create objects via AutoBoxing, which is the automatic conver-
sion of simple data types to the object-oriented types? Take the test:
Integer value_1 = 3;
Integer value_2 = 3;
boolean equality = value_1 == value_2;
System.out.println(“Objects are equal when AutoBoxing: “
+ equality);
The UML diagram from the PizzaFlyweight example project can be found in Fig. 13.1.
13.6 Summary
• You have a large number of objects that are either large, costly to create, or simply
consume a lot of system resources due to their number.
• You split these objects into a part that can be split and a part that is not split.
• The attributes that can be shared form the intrinsic state; they are flyweights.
• The extrinsic state is shifted to the context.
• The context manages the intrinsic and extrinsic state.
• Operations are performed by the context passing the extrinsic state as a parameter to a
method of the intrinsic state.
• The intrinsic state is independent of its context.
164 13 Flyweight
The Gang of Four describes the purpose of the pattern “Flyweight” as follows:
Use objects of smallest granularity together to be able to use large amounts of them efficiently.
Interpreter
14
After the first excursion to the structure patterns Composite and Flyweight, we continue
again with a behavior pattern, the Interpreter Pattern. The pattern is not really difficult to
understand, but it opens up great possibilities.
I would like to give you an impression of where the journey is going right at the beginning.
The screenshot in Fig. 14.1 shows the program from the sample project Interpreter, which
we will now discuss, in action. The principle is very simple: you type a mathematical
expression into the input field. It can be of any length and nested; you end the input with a
semicolon. Unary plus and minus, the addition sign, the subtraction sign, the multiplica-
tion sign and the division sign are processed. In addition, you can set round brackets to
indicate priorities. Square brackets indicate that the result will be processed as an absolute
value. You can insert as many spaces as you like. When you click on “Start”, the input is
scanned, parsed and calculated.
The input field is an editable combo box, in which a few expressions are already stored.
The structure of the GUI will not be discussed here; the code is very simple. When you
click on “Start”, the combo box queries the entered value. This value is passed to a scan-
ner, which returns a list of symbols. You send this list to the parser, which creates the root
of an abstract syntax tree. On this root you call the method calculate() and get the
calculated result.
This chapter deals with the question of how you prepare an expression that a user enters
in such a way that a computer can handle it. The tools of the interpreter pattern, i.e. the
abstract syntax tree, the scanner and the parser, are discussed.
The subject of compiler construction is the subject of an independent lecture at the
university within the framework of computer science studies. Reference books on this
subject contain many hundreds of pages. So, the implementation I will present to you can
only be one way of many. I have developed it with the motivation to offer a comprehen-
sible introduction; I have left out questions about efficiency.
14.2 The Scanner 167
The first processing step is performed by the scanner. It performs the lexical analysis. In
this process, the input is broken down into sense units, the tokens, or symbols. Symbols in
this example are numbers, spaces, semicolons, the parentheses, and the mathematical
operators. For example, suppose you enter 5 + 14;. The scanner recognizes the following
symbols:
I want to have the scanner filter out the spaces and the semicolon. So the scanner returns
the following symbols:
It is important to see that the scanner does not yet perform a syntactic check; it cannot
detect whether an opening bracket has also been closed again. In the following paragraph,
let’s look at how the scanner breaks a string into symbols.
In the package symbols, you first create the superclass Symbol. From it, symbol classes
are derived to represent the mathematical operators, parentheses, and numbers. Consider
the Add symbol-it doesn’t have to do anything more than say about itself “I am a plus sign”.
If the grammar would allow the input of strings, you would have to introduce a
literal symbol corresponding to the numeral symbol.
I have not printed the implementation of the toString methods in each case. You can see
that the isNumeral() and isPlus() methods each override a method of the Symbol
superclass. In Symbol, the is… method is negated by default for all derived symbols.
So you can ask any symbol if it is a plus sign, but only the plus sign will actually return
true. In the following paragraph you can see how the scanner generates the symbols.
In the class Scanner, it is mapped which character is mapped to which symbol. In addi-
tion, a list of symbols is defined, in which all symbols are stored; this list is returned later
by the scanner.
new HashMap<>();
private Scanner() {
operators.put('+', new Add());
operators.put('-', new Subtract());
operators.put('*', new Multiply());
operators.put('/', new Divide());
operators.put('[', new AbsoluteStart());
operators.put(']', new AbsoluteEnd());
operators.put('(', new LeftParenthesis());
operators.put(')', new RightParenthesis());
operators.put('=', new Equals());
}
// … abridged
}
The constructor is overloaded. You pass the string you entered to it. It removes the
spaces and throws an exception if the input is not terminated with a semicolon. It also
checks to see if there is more than one semicolon in the input. It then goes through the
string character by character and tries to convert the characters into symbols.
Consider below the work inside the for loop. The indexed character is extracted from
the string. Since the semicolon is not taken into account, the scanner only has to act if the
current character is not a semicolon. The scanner first checks whether the currently
indexed character is contained in the list of operators. In this case, the corresponding sym-
bol object is read out and stored in the result list. Then the scanner checks whether the
current character is a letter, a literal. You will need a letter if you want to develop the proj-
ect further and introduce variables or map an assignment: a = 5 + 7. In this version, literals
170 14 Interpreter
are scanned, but will not be parsed correctly. Instead, a usage of a literal may cause an
exception.
As soon as the scanner determines that the current character is a number, it creates a
buffer. It reads the next character into the buffer in a while loop until the following char-
acter is no longer a number. If the next current character is a point or a comma, it is also
copied into the buffer – it can be assumed that the user has entered a double value. Provided
there are decimal numbers, these are also queried in a while loop and passed to the buffer.
Finally, the cache is used to create a numeral symbol that is stored in the result list. Now
the work starts again at the beginning of the for loop with the next current character. Here
is the code in the for loop that I had cut out above:
This does the work of the scanner. It overrides the toString() method and defines
the getSymbols() method, which returns the result list.
14.3 The Parser 171
If you enter the string 2 + (6 + 4) * 4;, the scanner will return a series of symbols. These
symbols have not yet been checked to see if they are syntactically correct, e.g. if all brack-
ets have been closed. Also, no meaning has been assigned to the symbols yet. Both, syn-
tactic and semantic analysis are the task of the parser.
The GoF assumes that the expression is already parsed when describing the inter-
preter pattern. The interpreter operates on the parsed abstract syntax tree.
Nevertheless, I describe the parser in this section to introduce you to the interpreter’s
tools, the expressions.
Scanner and parser have the task to create an abstract syntax tree. What is meant by this?
A syntax tree has the task of representing a sentence in the defined grammar as a tree
structure. If you were to include all spaces and the semicolon in the tree, you would have
a concrete syntax tree. A concrete syntax tree is not efficient to manage. Therefore, to
generate an abstract syntax tree, you go one step further and remove all symbols that are
not required for execution.
The spaces and the semicolon are already removed by the scanner. What about opera-
tors and numerals? Each operator and numeral is represented by objects that are the nodes
of the syntax tree. The edges of the tree describe the composition of the nodes. Take the
example from above; the user types: 5 + 10;. The scanner removes the spaces and semico-
lon and returns number (5) plus sign (+) number (10). The diagram in Fig. 14.2 illustrates
how the expression is converted into a syntax tree whose nodes are the operators and
numerals.
The priority of the point calculation over the dash calculation can be taken into account
by the position in the syntax tree. The input 10 + 5 * 4; would produce the syntax tree in
Fig. 14.3.
Brackets can change the priority. The changed priority is taken into account in the syn-
tax tree, so that the brackets can be removed. For example, the user enters: (10 + 5) * 4;.
This input can be represented by the syntax tree in Fig. 14.4.
In the next section, we’ll look at how to convert a sequence of symbols into a syn-
tax tree.
Operators and numerals are nodes of the syntax tree. To be able to treat them the same, it
is necessary that both are of the same type. Group numerals and operators under the inter-
face Expression, which the method calculate() prescribes.
A numeral does not reference any other expressions. It may be limited to storing and
returning the value when calculate() is called.
@Override
public double calc() {
return value;
}
}
14.3 The Parser 173
Operators operate on two expressions. They are non-terminal and are grouped under
the abstract class NonTerminalExpression.
To add two numbers, derive the PlusExpression class from this class. You pass the
two expressions to be added to it. The calc() method is implemented so that the
PlusExpression asks the two expressions to return their values. These values are added
together and the sum is returned.
@Override
public double calc() {
return left.calc() + right.calc();
}
}
At the beginning, your grammar should be limited to the fact that two or more numbers
can be added.
The sample project Interpreter_1 is a slimmed down version of the project Interpreter. The
parser stored here is limited to adding any number of numbers. Let’s have a look at the
procedure. You pass the list of symbols generated by the scanner to the constructor. The
parser lets itself be given the iterator from this. The method nextSymbol() determines
the next symbol from this list, which is stored in the attribute currentSymbol. If there
are no further symbols, an EndSymbol is passed to currentSymbol. The constructor
passes an expression to the root field, which is returned by the parseExpression()
method. The client can query root with getRoot().
174 14 Interpreter
So we actually already use several patterns in combination here: The iterator you saw
in Ch. 11 and the composite known from Ch. 12. Feel free to look it up again and try to
find these structures here.
The method parseExpression() is the entry point for the parser. It calls the
method parseAddition(), which returns an object of type Expression.
The method parseAddition() queries the first summand in the first step and calls
the method parseNumber() for this purpose. If parseNumber() determines that
currentSymbol is not a number, the method throws an exception; otherwise, the value
of currentSymbol is passed to a TerminalExpression and returned. Before that, cur-
rentSymbol is updated to the next symbol in the list of symbols. The variable expres-
sion in parseAddition() now references a TerminalExpression. The
parseAddition() method now loops to check if currentSymbol is a plus symbol.
If this is the case, the symbol is no longer needed, the next symbol from the list of symbols
can be passed to currentSymbol. From parseNumber() the next number, a termi-
nal expression, is queried. With the two expressions a PlusExpression is created and passed
to expression. The variable expression is finally returned.
14.3 The Parser 175
var value =
Double.parseDouble(currentSymbol.toString());
nextSymbol();
return new TerminalExpression(value);
}
Tip
If you type 5 + 7–2;, the parser generates a series of expressions that are partially
nested. Using a debugger or suitable console output, try to see that the expressions
are nested as follows:
MinusExpression (
PlusExpression (
Terminal ( 5.0 ) Terminal ( 7.0 )
)
Terminal ( 2.0 )
)
The abstract syntax tree for this can be found in Fig. 14.5.
In the next section we add the point calculation, which must be considered before the
dash calculation.
The version in the sample project Interpreter_3 includes the multiplication and division.
Two expressions are added: the MultExpression and the DivExpression. Accordingly, the
parser knows the method parsePunktrechnung(), which is structured similarly to
parseMultDiv(). Multiplication and division have priority over addition and subtrac-
tion. To establish this precedence, the parseAddSub() method must first call parse-
MultDiv() before checking whether to add or subtract.
Tip
Try to reproduce, either with a debugger or with console output, that 5 * 2 + 3; is
parsed into the following expression:
PlusExpression {
MalExpression (
Terminal ( 5.0 ) Terminal ( 2.0 )
)
Terminal ( 3.0 )
)
PlusExpression (
Terminal ( 5.0 )
MalExpression (
Terminal ( 2.0 ) Terminal ( 3.0 )
)
)
178 14 Interpreter
Now let’s move on to the Interpreter_4 sample project. Here you will first consider a
sign-minus and a sign-plus. Does a sign-plus make sense? No idea – but in any case, it is
mathematically correct, and our grammar should allow it. In priority, the sign still comes
before the multiplication and division. So you need to check it before division and multi-
plication. The parseMultDiv() method, when called, must first pass the call to the
new parseLeadingSign() method. If parseLeadingSign() detects that a sign-
minus is set, the next TerminalExpression is wrapped in a UnaryExpression. The code
isn’t much more complicated than the previous versions of the project; so I won’t explain
it in detail. Just look at the mentioned methods in the parser class.
In this section, parentheses are introduced. Now we come full circle and back to the
Interpreter sample project from the beginning of this chapter. Round brackets set the pre-
cedence of a calculation; square brackets cause the bracketed expression to be positive.
Parentheses have a higher precedence than the sign, so they must be parsed beforehand.
The parseAbsolute() method parses an expression that is enclosed in square brack-
ets, and the parseParenthesis() method parses round brackets. The first change is
found in the parseLeadingSign() method: When a parenthesis is placed around an
expression, that expression can be a NumeralExpression or a nested expression that can
consist of addition, subtraction, multiplication, and division. Thus, an UnaryExpression
may only be created if the following symbol represents a number.
wrapped into an AbsoluteExpression and returned. The AbsoluteExpression has the task
of determining the amount of the referenced expression. In the above test, if the cur-
rentSymbol is not an opening square bracket, parseAbsolute() asks the parse-
Parenthesis() method for an Expression object.
From the example project Interpreter_4 we have to split the UML diagram a bit due to
its size.
In Fig. 14.6 you first see the classes from the package symbols.
The classes from the package expressions can be found in Fig. 14.7.
And the classes from the package interpreter itself can be found in Fig. 14.8.
I wrote at the beginning that the GoF in describing the interpreter pattern assumes that the
expression already exists as a finished parse tree. So, what is the basic principle of the
interpreter?
They have the common interface Expression. Terminal and non-terminal classes are
derived from this interface. Each non-terminal class represents a rule in the grammar; the
operands – numbers in the example above – are stored in instances of terminal classes. The
Fig. 14.6 UML diagram of the interpreter pattern. (Example project Interpreter_4, package
symbols)
Fig. 14.7 UML diagram of the interpreter pattern. (Example project Interpreter_4, package
expressions)
14.5 Discussion of the Interpreter Pattern 181
Fig. 14.8 UML diagram of the interpreter pattern. (Example project Interpreter_4, package
interpreter)
nonterminal classes reference subexpressions that are themselves either terminal or non-
terminal. It follows that any expression to be interpreted must be able to be represented as
an abstract syntax tree. The syntax tree is composed of instances of the defined terminal
and non-terminal classes.
The client calls the method defined in the common interface at the root of the syntax
tree. Each nonTerminalExpression calls the same method on the referenced expressions,
processes the response of the expressions, and returns the result. Does this approach look
familiar from the Composite Pattern? The similarity is striking, isn’t it? The GoF describes
Composite and Interpreter as having “many implementation aspects in common”. On the
Internet and in the literature, you will occasionally even read the opinion that Interpreter
is just a special case of Composite. However, you will notice differences in purpose and
implementation. The composite is a structure pattern that can map a tree structure, whereas
the interpreter is a behavior pattern that performs processing across the structure in our
examples.
182 14 Interpreter
14.6 Summary
The Gang of Four describes the purpose of the “Interpreter” pattern as follows:
For a given language, define a representation of the grammar and an interpreter that uses the
representation to interpret sentences in the language.
Abstract Factory (Abstract Factory)
15
Do you remember the singleton pattern from Chap. 3? You created an object without using
the new operator. Instead, the client could use a static access method to get the instance of
the class. The abstract factory delegates the creation of objects to a method, so that you,
the user, can do without the actual creation with new.
Using two examples, let’s take a closer look. We start with a beautiful garden.
In our example there are different types of gardens; first we have monastery gardens where
herbs are planted. But there are also ornamental gardens where roses grow. The respective
enclosure is different, of course. The monastery garden is surrounded by a stone wall,
while the ornamental garden is framed by a nicely cut hedge. The ground is also different
in each case: in the monastery garden there are stone slabs, while in the ornamental garden
you walk on fresh grass. The monastery garden and the ornamental garden are therefore
two different product families. Each product family has the same characteristics (plants,
fencing, soil), but each has a different design; the respective characteristics are called
products in the abstract factory.
Let’s start with the example project Abstract_Garden, with the package Trial1, where we
define a class Garden, where the garden is created and maintained. Like this:
You can guess that this approach seems somehow wrong. But why are you bothered by
it? Quite simply: You have three methods in which a switch query is needed. If you want
to create another garden – for example a kitchen garden with tomatoes – you have to
change a switch query in three methods. This is not very maintenance-friendly, but all the
more error-prone.
The garden certainly also needs to be managed: The plants want to be watered and
pruned; you also need to weed regularly. I will not print the methods for managing the
garden here. However, you have a violation of the Single Responsibility Principle here:
15.1 Create Gardens 185
you put the creation of the garden and its maintenance into one class. True, I had written
that it is okay to violate it if you are aware of it and have a good reason. But that good
reason is exactly what is missing here. Violating the SRP could be problematic, though,
because both creating and managing the garden are very costly; these two tasks had better
not be mixed. So the approach above implements a lot of code in a single class that, in case
of doubt, is not needed at all. If you redefine a responsibility, both responsibilities need to
be retested. Neither of the responsibilities can be reused. Here, the cohesion is decidedly
weak and that is an indication of inappropriate design.
Perhaps the problem would be solved if you used inheritance? In the Trial2 package, you
define an abstract class AbstractGarden, from which the subclasses
MonasteryGarden, KitchenGarden, or OrnamentalGarden inherit. The gar-
den is created in the constructor of the subclass.
This solution looks cleaner, but in fact it doesn’t solve the dual responsibility problem:
you still have an object that lavishly creates the garden and manages it. Moreover, this
approach violates a principle that I consider much more essential than the SRP: prefer
composition to inheritance. So let’s find an alternative!
In the Trial3 package you have different gardens (product families) each with different
products, i.e. different types of plants, soil types and enclosures. The products vary and are
now encapsulated. I mentioned earlier that it makes sense to program against interfaces.
This gives you the greatest possible flexibility. How can the products be abstracted? Both
herbs and roses are plants; both the stone wall and the hedge are enclosures; both the flag-
stones and the lawn are soil. So design the interfaces, the different abstract products, and
the implementations, the different concrete products. As an example, let me show
the plants.
186 15 Abstract Factory (Abstract Factory)
To create the different gardens, define a factory. For a monastery garden, implement a
MonasteryGardenFactory and for the ornamental garden, implement an
OrnamentalGardenFactory. You expect both factories to provide the same methods
and create the same products. To ensure this, you define an interface (abstract class or
interface), the abstract factory, from which the concrete factories inherit. And in it are the
methods that must be implemented in each of the concrete factories.
@Override
public Floor layFloor() {
return new Flagstone();
}
@Override
public Enclosure enclose() {
return new StoneWall();
}
}
15.1 Create Gardens 187
And how can the client who wants to have a garden deal with this? It creates an instance
of the desired garden factory and gets the individual elements from it. In the following
example, we create the elements for a monastery garden.
Note that I explicitly specify the superclass AbstractGardenFactory for the dec-
laration of the variable fabrik. Using var. for this declaration would create a factory
of type MonasteryGardenFactory, which I explicitly do not want here.
Let’s look at what we gain from this.
For a better overview, the class diagram Fig. 15.1 only shows the project in abbreviated
form – the floor classes end interface are left out here.
The abstract garden factory knows the abstract declaration of the individual products,
i.e., the interfaces floor, enclosure and plant. The client creates a variable of the abstract
factory type that references an instance of a concrete factory: AbstractGardenFactory
factory = new OrnamentalGardenFactory(). The concrete factory knows its
specific products: In the example, the ornamental garden knows only the lawn, rose, and
hedge – it has no reference to the flagstones, herb, or stone wall. When the client requests
floor, plants, and enclosure, the factory is able to return its special products. Since the client
relies on abstractions, i.e. the interfaces, it has no idea what specific products it will receive –
and thanks to polymorphism, it doesn’t have to worry about the runtime type of the object.
The client is free to instantiate a different factory and thus be given completely different
products. A single line of code changes the nature of the garden:
AbstractGardenFactory factory =
new MonasteryGardenFactory();
You have a cohesive family of products and you can add new families very easily. What
would have to happen if you wanted to create a new garden, for example an allotment or
garden plot? First, you need a new concrete factory that implements the
AbstractGardenFactory interface. Take a look at the example project GardenPlot:
@Override
public layFloor() {
return new ConcreteApron();
}
@Override
public Enclosure enclose() {
return new ChainLinkFence();
}
}
15.3 Chasing Ghosts 189
You now still create the concrete products, the classes Tomato, ConcreteApron,
and ChainLinkFence, which must correspond to the interfaces of the abstract prod-
ucts, Floor, Enclosure, and Plant.
Why is the pattern called “Abstract Factory”? Because both the factories and the
products rely on abstraction.
You will use the abstract factory whenever you need a set of individual products that all
correspond to a certain type. The products form the product family. In the previous exam-
ple you got to know the product families of the gardens. However, the following example
would also have been conceivable: You are programming an address management system.
When you code the postal code, you plan five digits for Germany. But the structure of a
postal code will be different in America or in Russia. Likewise, the length of the telephone
number varies from country to country. Maybe there is even a different structure or a cer-
tain pattern for it? So, when you instantiate a contact, you need a consistent set of products
for each country: a country-specific postal code and a country-specific phone number. The
AmericaFactory will provide these products, just like the RussiaFactory, but
customized for that country. The contact generated by the factory, if the factory is pro-
grammed correctly, is consistent. Furthermore, the client can be written in such a general
way that its code is valid for all conceivable product families.
Where do you find the abstract factory in practice? Think of the different look and feels
that Java provides. You can set a specific look and feel and change it at runtime. Each com-
ponent is drawn uniformly with the chosen look and feel. Third party developers can
develop their own look and feel. And since all created objects come from the same family,
they can interact with each other if necessary. One pitfall must be addressed, however: Once
you’ve determined the products, stick to them. Imagine that you also want to create a pond
in your garden. The AbstractGardenFactory interface must dictate a corresponding
factory method getPond(). Then, all concrete factories must define a method that returns
a pond object. In our small example, this is certainly not a problem. However, if you have
created a large framework for which your clients have already created their own garden
factories, then this change can become very high-maintenance and therefore very expensive.
Imagine you get the order to create a game in which you have to hunt ghosts and open
magic doors in an old house. Remember the old text adventures that were popular in the
early days of home computing and still have fans today? We’re doing something similar.
190 15 Abstract Factory (Abstract Factory)
The first version you create should be quite simple. You have a house created and explore
it with a few simple commands. The commands you enter will be marked with the greater
than sign, which should appear after the description of the game situation. Example:
Example
The first version has nothing to do with factories yet. It is intended to introduce you to the
logic of the game and the approach to programming. We start with the example project House_1.
// … abridged
@Override
public String toString() {
return description;
}
}
A wall cannot be entered – at least in my version; but you might want to elaborate the
project further and create walls that can be moved and clear the way to a mysterious pas-
sage. For my version, the wall can be limited to emitting an error message when the
enter() method is called. However, it must be able to describe itself.
@Override
protected string describe() {
return “ a wall.“;
}
}
A door receives references to the two rooms it connects in the constructor. If the door
is open, it can also be entered, i.e., the user walks through it.
void open() {
System.out.println(“The door is opened“);
isOpen = true;
}
@Override
public void enter(player player) {
if (isOpen) {
var currentRoom = player.getCurrentRoom();
15.3 Chasing Ghosts 193
@Override
protected string describe() {
return isOpen
? “ an open door“ : “ a closed door.“;
}
// … abridged
}
The class Room expects a name and a description in the constructor. Both are needed
to generate meaningful text output. In an EnumMap, the directions are stored as keys; the
values are components attached to a side: Doors or Walls. The overloaded method
addComponent() allows to insert a door or a wall at a side of the room. When a room
is created, all sides are initially pre-populated with walls, and a door can only be subse-
quently placed at a location where there are walls in both rooms.
This is physically not quite correct, because the wall between two rooms exists only
once. Here in the example, we look at it from each room individually.
Of course, you can also enter a room. The method outputs a description of the current
room and informs the player about his current position.
@Override
public void enter(Player player) {
System.out.println(“You are now “ + description);
player.setCurrentRoom(this);
}
The other methods of the class describe the room. If you want to expand the project
further, you can provide trap doors or a staircase or even a chimney through which the
room can be changed, according to this procedure.
When processing the commands, you resort to the Command pattern. There is the
Command interface, which specifies the matches() and handle() methods. The
matches() method is passed the entered line; the method checks whether the entered
string matches the Command object. The entered command is represented by the static
inner class CmdLine, which internally tokenizes the command.
The handle() method of the Command interface describes the action to be per-
formed by the command. As an example, I print the source code of the exit command.
But please also have a look at the sample code to see how the system reacts to missing
directions for the commands “open door” and “enter door”.
196 15 Abstract Factory (Abstract Factory)
@Override
public void handle(CmdLine cmdLine, Player player) {
System.exit(0);
}
}
The while loop mentioned above passes the command entered to an object of the class
CmdLine. Subsequently, all commands are checked in a for-each loop to see whether
they correspond to the command entered. If they do, the command is executed.
Please make sure to analyze the complete source code of the class House in the exam-
ple project House_1.
The second version of the project is primarily intended to resolve the complexity of the
class House of the first version. The class has three tasks: It must obtain the components
of the house, it must assemble the components, and it must provide game control. The
15.3 Chasing Ghosts 197
multiplicity of tasks violates the SRP! In fact, it should be enough for the class to know
which room the player is currently in in order to apply the currently entered command to him.
To break the complexity, the construction process is outsourced in this version of the
program, which you can find in the example project House_2. There is a class
ComponentFactory whose task is to create rooms, new walls and new doors from the
components.
In addition, in this program version there is the new class Architect, which knows
the factory and gets rooms and doors from there to construct the house and assemble it
from the individual components. If the floor plan is stored in a file that needs to be read and
parsed, the Architect class can become quite complex. The architect gets a reference
to a ComponentFactory in the constructor. From this factory it gets the components
and assembles them. Afterwards, it returns the room that you defined as the entry point.
// … abridged
Room buildHouse() {
var corridor = factory.createRoom(“corridor“,
“in the corridor“);
var hallway = factory.createRoom(“hallway“,
“in the hallway“);
// … abridged
var door = new Door[7];
door[0] = factory.createDoor(hallway, corridor);
// … abridged
door[6] = factory.createDoor(livingroom, study);
board.addComponent(direction.NORTH, door[0]);
// … abridged
return hallway;
}
}
The House class has now become very lean. It can limit itself to creating an architect,
parameterizing it with a component factory and having it build a house. The class
House only needs to know the entrance and the architect tells it.
198 15 Abstract Factory (Abstract Factory)
private House() {
architect =
new Architect(new ComponentFactory());
entrance = architect.createHouse();
/ … abridged
}
In this version, we have introduced one factory; in the following section, we create a
second factory.
In the third version you will find the principle of the Abstract Factory Pattern again.
However, the abstract factory is not so abstract at all, as you will see in a moment. Now
take a look at the example project House_3.
15.3.3.1 Spells
New in this version is a class that defines a door that can only be opened with a spell. When
you try to open a door, it will ask you a question. For example, you may want to open a
door and it will tell you to “Speak FRIEND and enter”. Of course, if you are familiar with
the relevant literature, you will know to type mellon. The question and your answer are
stored in the static inner class question.
@Override
CreateDoor(Room room1, Room room2) {
var number = (int) (Math.random() * 10);
if (number > (questions.size() - 1))
return new Door(room1, room2);
else
return new EnchantedDoor(room1,
room2, questions.get(number));
}
}
So, the factory class is very unspectacular. Now let’s look at the door with spell.
@Override
void open() {
if (super.isOpen())
System.out.println(“… “);
else {
var scanner = new Scanner(System.in);
System.out.print(question.question + “ >> “);
var input = scanner.nextLine();
if (input.equalsIgnoreCase(question.answer))
super.open();
else
System.out.println(“… “);
}
}
}
In the next version, you will introduce another factory that will generate rooms where
you will encounter ghosts.
Our goal is to program a haunted house. There are doors that have to be opened with a spell.
There are also some rooms where ghosts can be found. So, to program the last version,
create the class HauntedHouseFactory, which inherits from the class
EnchantedDoorFactory. That is, randomly create doors with spells. You apply the
same logic again to create rooms where ghosts can randomly appear. A static data field is used
to store how apparitions can appear. You can find the code in the example project House_4.
The factory should be able to create a room with a ghost. The class HauntedRoom
extends the class Room. When you enter the room, it is randomly decided whether the
ghost appears or not. A static data field stores how the apparitions can appear.
At the end of the project I would like to mention a difference to the garden project.
There, we had abstract products, the interfaces Plant, Floor and Enclosure.
In the Ghost House project, the abstract products, the classes Door, Room and
Wall, are also concrete products. Further concrete products are derived from these
products, e.g. the enchanted door or the haunted room. In the same way, the
ComponentFactory is an abstract and a concrete factory at the same time – new
factories are derived from this factory. This is also a possible implementation of the
Abstract Factory Pattern. I use inheritance instead of composition because this is
actually extending behavior of an existing class. A HauntedRoom is a Room.
202 15 Abstract Factory (Abstract Factory)
Figure 15.2 shows the UML diagram from the example project House_4.
15.5 Summary
• With the abstract factory, you create a product family that consists of various individual
products.
• The products together form a unit, e.g. a uniform look and feel.
• During implementation, an interface for a factory is created: the abstract factory.
• The client programs against abstractions of products, that is, against abstract products.
• The abstractions can be abstract classes or interfaces.
• A concrete factory produces the concrete products that belong to a particular family.
• The bond between the products and the client is loosened.
• As a result:
• An entire product family can be replaced without affecting the client code,
• A specific product can be changed without affecting the client code,
• New product families can be added,
• Products can be reused, e.g. in other product families,
• The products can communicate with each other because they know their mutual
interfaces.
• One disadvantage is that it is difficult to expand a product family with additional
members.
The Gang of Four describes the purpose of the “Abstract Factory” pattern as follows:
Provide an interface for creating families of related or interdependent objects without naming
their concrete classes.
15.6 Description of Purpose 203
Fig. 15.2 UML diagram of the Abstract Factory Pattern. (Example project House_4)
Factory Method
16
In Chap. 15 you got to know the abstract factory. If you now work on the factory method
pattern, you can transfer many structures and arguments from there. While both patterns
are about creating objects, they differ in their goals; the abstract factory creates unified
product families, while the factory method creates only a single product. They also differ
in structure – the Abstract Factory is based on object composition, the Factory Method is
based on inheritance, as you’ll see in a moment.
For the first, introductory example, meals are to be prepared. Since everything is kept very
simple at the moment, there are only the classes Doner and Pizza. Both implement the
interface Meal. Fittingly, there is a Pizzeria and a Takeaway. Both inherit from the
abstract class Restaurant. The abstract class defines the order() method, which
dictates that the guest should first be asked for their request, then the requested meal is
prepared, and finally it is served. This method is called by the guest when he wants to order
a meal. The method prepareMeal() is a factory method. Its job is to create a specific
product. The actual execution is delegated to subclasses, i.e. Takeaway and Pizzeria.
Note that while the takeOrder() method is also defined by the subclasses, this is irrel-
evant to the discussion of the pattern; I intended this coding to ask the guest for their pizza
request at the pizzeria and their doner request at the takeaway. You can find the complete
code in the sample project Meal.
Takeaway and pizzeria create either a doner or a pizza – each varied according to cus-
tomer preferences. All meals implement the same interface. The guest – in the example the
test class – creates the instance of a subclass of the class Restaurant and calls the method
order() on it.
If the guest wants to eat a pizza, he creates an instance of the class Pizzeria,
otherwise an instance of the class Takeaway. At no point is there an if query. Just
by selecting the “right” subclass, the desired product is created.
Figure 16.1 shows you the class diagram of the project. In the terminology of the pat-
tern, the interface Meal is a product. The derived classes Doner and Pizza are concrete
products. The interface Restaurant is a producer and the concrete localities (pizzeria
and takeaway) are concrete producers.
The factory method can optionally be parameterized. The project is already prepared for
this variation. The method takeOrder() returns a string with the order. The previous
project version ignores the customer request, which is not very friendly. In the new ver-
sion, the order is passed to the factory method as a parameter, which then creates the
desired meal. The pizza example demonstrates this procedure. The following code from
the MealVariation sample project shows the parameterized factory method of the pizzeria.
Note that I added a switch expression to the return statement in the else branch, and
then I don’t have to write another return in every single case, but give the result expres-
sion of every case distinction to the return statement. In the above example you can also
see that firstly you can have multiple statements executed in a case distinction (here the
208 16 Factory Method
default case), and secondly the keyword yield which is used instead of a return. A
return is intended exclusively for terminating a method. The termination of a command
sequence within a switch expression is done with yield and returns the subsequent value
as the value of the expression.
With a parameterized factory method, you can easily introduce new products. Without
parameters, you would have to rely on creating a new concrete producer for each new
concrete product. However, since the production effort for the different pizzas is similar
and calzone even inherits from pizza, it makes sense to parameterize the factory method.
In Chap. 11, you created iterators. To use a collection in an extended for-each loop, it must
implement the Iterable interface. The interface prescribes the iterator() method,
which returns an object of type Iterator. The Iterable is implemented by the
ArrayList class, for example, but the two collections you created in the Iterator Pattern
chapter also have this interface implemented. All of these classes must be able to return a
product, an object of type Iterator; the Iterator prescribes three methods that you can
use to iterate element-by-element through the collection. In most cases, it might be conve-
nient to define the iterator as an anonymous class, that is, return new Iterator{...}.
The diagram in Fig. 16.2 shows this interaction.
You define an interface, the Iterable interface, which prescribes a method for creat-
ing objects: iterator(). This method is the factory method. An object of the type of
the interface Iterable must return an object of the type Iterator, which works
together with its own specification. In the diagram, I have helpfully named this object
MyIterator. The example project Iterator shows this (rough) structure.
The factory method pattern is useful when you want to write general-purpose code. A
for-each loop, which I mentioned in the last example, has no idea from which class the
16.3 Practical Application of the Pattern 209
object of type Iterable was instantiated – it doesn’t even need this knowledge.
Similarly, the for-each loop has no interest in which concrete product it is working with.
Since the binding of the concrete iterator object to the for-each loop is very loose, you can
define any collection classes and use them in the for-each loop. The concrete creator is
responsible for creating a matching concrete product.
If you create a new object with the new operator, you must always call a constructor of
the class. When you define a factory method, on the other hand, you can choose any identi-
fier you want. For example, consider the procedure of the Color class. Here, there are
numerous static methods whose only task is to create a Color object from certain param-
eters. There is, for example, the method getHSBColor(), which describes its purpose
much more succinctly.
If you compare the Factory Method pattern up to this point with the Abstract Factory,
you’ll notice that the Abstract Factory is much more complex. In fact, many projects
start with the Factory Method and end up with the Abstract Factory.
In the Abstract Factory chapter, when you programmed the Haunted House, you referred
back to the Factory Method several times. There was the creator, the class
ComponentFactory. In this class, there are the methods createRoom(), creat-
eDoor(), and createWall(), which all create doors, rooms, and walls that have no
special feature, that is, neither a ghost nor an enchantment. This class is the interface and
there is nothing wrong with defining the interface to bring default behavior. Therefore,
other classes, called concrete creators, were derived from this class; one class creates
doors with spells. The other class creates doors with spells and rooms with ghosts. The
derived classes override certain create methods of the superclass. These are exactly the
“factory methods”. It looked similar with the products. Products were the classes door,
room and wall. From these, other concrete products were derived: Doors with spells and
Rooms with ghosts.
210 16 Factory Method
You create a framework that manages appointments and contacts. In the screenshot in
Fig. 16.3, you can see that I have created several contacts. I also have an appointment with
my editor on July 22, 2022 – I must not forget it!
The frame of the program provides a blank interface and the menu. You can create
entries (appointments and contacts). If you have created a new entry, it will be displayed on
a new tab. The program should remain flexible to be able to display new entries such as
e-mails or entire address books in a further expansion stage. You can guess that it’s all about
the frame and the entries being separated from each other and having only loose interfaces.
Let’s take a closer look at the connection between the frame and the entry. Entries are
certainly responsible for their own data. A contact consists of first and last name and a date
16.4 A Larger Example – A Framework 211
of birth. An appointment consists of the name of the other participant and the date of the
appointment. The question now is which entity composes the controls for each data. If you
delegate this task to the frame, this assumes that the frame knows all the dates of all con-
ceivable entries. You are restricting yourself in that an existing entry may not get any
additional data, because otherwise the frame would have to be modified. Also, implement-
ing new entries would be extremely costly because the new controls would have to be
coded in the frame. However, the frame has already been extensively tested and delivered
to the customer – you will never want to touch it again under any circumstances. So the
responsibility for designing the editor must be delegated to the class that best knows the
controls you need: the entry itself.
You can find the following code in the AddressBook sample project.
The entries implement the interface Entry. It prescribes two methods; one requests a
textual description from the entry, the other retrieves an object of type EntryPanel.
The returned EntryPanel can be optimized for either an appointment or a contact – this
is like the iterator returned by any list with its own specifics.
The editor of an entry is of the type of the interface EntryPanel. This interface again
prescribes two methods. One method asks the editor to save the entry. The other method
returns a JPanel on which the controls are arranged.
Next, I would like to introduce the Contact class as an example of a possible entry. The
class stores the first name, last name, and date of birth in its data fields. In addition, the
class creates an object of type EntryPanel. This object is an instance of an inner class,
which will be described below.
212 16 Factory Method
@Override
public EntryPanel getEntryPanel() {
return entryPanel;
}
In the constructor of the inner class, the editor panel is assembled. Here I use the
TwoColumnLayout that you developed in the chapter about the Strategy Pattern. Further,
I’ve given the input fields a FocusListener that highlights all the text when you click in the
field. And finally, the JSpinner, where the date of birth is entered, gets its own date editor
and date model. Please analyze the code of the project. Of course you could also use a
DatePicker instead of the simple spinner, this is not available natively in the JDK, but in
many free libraries, of which you can choose one. For the demo here, however, the JSpinner
serves its purpose. You can also overwrite the date manually instead of tediously “spin-
ning” back and forth day by day.
The input fields are saved as data fields. When the user gives the command to save the
data, the editor transfers the values from the input fields and from the JSpinner to the data
fields of the outer class. If you extend the project further, you could, for example, provide
the functionality that the class gets serialized. The getEditor() method returns the panel
built in the constructor.
MyEditor() {
// … abridged
}
@Override
public void save() {
16.4 A Larger Example – A Framework 213
firstName = edtFirstName.getText();
lastName = edtLastName.getText();
birthday = (Date) spnBirthday.getValue();
}
@Override
public JPanel getEditor() {
return pnlEntry;
}
}
In this class there is a list where all entries are stored. Each new entry gets its own tab in a
JTabbedPane on the GUI. When the user clicks Save on the menu, an object of type
Action compares each entry in the list with each entry with the components of the
JTabbedPane. From the currently selected component, the editor is determined and the
save() method is called on it. The constructor assembles the controls and displays the
GUI on the screen.
Most of the tricky bits in this class are in the Swing programming area. What is impor-
tant for the factory method pattern is that the coupling of the client to the entries is loose,
which allows you to develop more entries very easily.
You can see the power of the factory method in this example: the client does not
need to know from which class the object of type EntryPanel was instantiated. It
is enough that it calls the factory method getEntryPanel() on the object of
type Entry. Since interfaces are classes with only abstract methods, you can say
that the entry (contact or inheritance) has delegated the responsibility for creating
the EntryPanel to a subclass.
Finally, let’s look at the differences with Abstract Factory. At first glance, the difference
seems relatively clear: The abstract factory creates a family of products; think of a unified
look and feel or a specific garden. The factory method creates a single product. However,
since nowhere does it say that a family must necessarily consist of two or more products,
the difference should not be based on this alone.
The more important difference, in my opinion, is that the Abstract Factory is object-
based, while the Factory Method is class-based. Object-based patterns rely on the interac-
tion of objects selected by the client. Class-based patterns involve inheritance; the
superclass calls the “right” method.
Background Information
I told you in Sect. 1.2 that patterns are divided into three categories: Creation
Patterns, Behavior Patterns, and Structural Patterns. However, the GoF has made
two further distinctions within these three categories: by scope.
Class-based patterns are the template method and the interpreter. In the case of
the adapter, there are two types – one object-based and one class-based. All other
patterns are object-based. So the GoF follows its own postulate that composition is
preferable to inheritance. An overview of the pattern categories can be found in
Table 1.1.
From the example project Address Book you can find the UML diagram in Fig. 16.4.
16.7 Summary 215
Fig. 16.4 UML diagram of the Factory Method Pattern (example project Address Book)
16.7 Summary
• The Factory Method supports SRP: object creation and object usage are separated.
• The object creation is outsourced to a separate class.
• The creator delegates object creation to a subclass.
• To do this, it calls the factory method that gives it its name: e.g. createCar().
• The subclass must define this method in its own unique way.
• The subclass decides which concrete product/object is created.
216 16 Factory Method
• Producers and products rely on abstractions and are therefore interchangeable for
the client.
• Three variations are conceivable:
–– In addition to the Factory Method, the producer can define other methods that are
applied to all generated products before they are returned,
–– The creator can provide a default implementation in the factory method,
–– The factory method can be parameterized.
The Gang of Four describes the purpose of the “Factory Method” pattern as follows:
Define a class interface with operations to create an object, but let subclasses decide which
class the object to be created is from. Factory methods allow a class to delegate object cre-
ation to subclasses.
Prototype
17
The Prototype Pattern belongs to the category of generation patterns. You have a certain
number of products that do not differ significantly. You create a prototype of a product.
When variations are requested, you clone the prototype and vary it. Example: you offer
different types of pizzas. To cope with the volume of orders, you make a prototype of a
Pizza Margherita. When a Hawaiian pizza is ordered, you clone the prototype, top it with
ham and pineapple, and serve the finished pizza. That actually explains the pattern. In this
chapter, I present more details and address the question of how to clone objects in Java.
The Prototype Pattern is not really difficult to understand – the problem is more about how
to clone an object. Take a look at the example project car factory. There we have the class
Motor with the attributes id (of the block), hp. (horsepower), and (cubic) capacity
and the respective access methods. The class Car holds a reference to an engine instance,
Car stores the number of seats and finally the special edition of the car. The class also
implements the interface Cloneable and overrides the method clone() of Object.
this.edition = edition;
this.numberSeats = numberSeats;
}
// … abridged
@Override
public Object clone()
throws CloneNotSupportedException {
return super.clone();
}
}
The main method of the test class demonstrates the use of the project. First, an engine
is created and installed in the car designed as a prototype. The prototype is to be created in
the TINY edition.
The solution works, but it is not unproblematic. Look critically at the first approach.
The project continues to compile without errors. Now call the test method again. You
will see that a CloneNotSupported exception is thrown. The default implementation
of the clone() method first checks whether the object to be cloned is of type
Cloneable. The interface is purely a marker interface – it does not impose a method. If
the object to be cloned is not of this type, the default implementation throws the exception.
If the object to be cloned is of type Cloneable, a bitwise copy of it is created and
returned. The type of the clone is the same as the type of the original.
The documentation for the Object.clone() method recommends that the original
object and the clone have the following relationships:
The default implementation of the equals method in the Object class checks for refer-
ence equality of the objects being compared (prototype == clone). However,
since prototype and clone have different references, if you override clone(), you will
also override equals(). If you override equals(), you should also override hash-
Code(). The documentation for Object.equals() says:
The equals method for class Object implements the most discriminating possible equivalence
relation on objects; that is, for any non-null reference values x and y, this method returns true
if and only if x and y refer to the same object (x == y has the value true).
Note that it is generally necessary to override the hashCode method whenever this method is
overridden, so as to maintain the general contract for the hashCode method, which states that
equal objects must have equal hash codes.
motor = prototype.getEngine();
motor.setId(“Marshall Motors“);
System.out.println(“Prototype: “ + prototype);
System.out.println(“New car: “ + newCar);
A change that you have made to the prototype is reflected in the clone. You have to criti-
cally question whether this is intentional. All employees of a company – the prototypes
and the clones – have the same employer, and if he is called “Dr. Z” today and “Dr. Q”
tomorrow, this affects all employees. Here, a change to a referenced object of a prototype
is certainly important for all clones. However, the reverse case is also conceivable: Dolly1
and the prototype sheep may have the same DNA. However, it must never happen that you
shear Dolly and the original sheep then also stands shaven on the meadow. In this case,
changes to the clone or the prototype must have no effect on the other specimen. But first
let’s look at why the engine is the same on both cars, even though you only made the
change to one.
1
https://de.wikipedia.org/wiki/Dolly_(sheep).
17.1 Cloning Objects 221
This standard implementation copies the object to be cloned bit by bit. The contents of
the variables are copied unchanged. What happens to a variable with a primitive data type?
At the point where you create an int variable, for example, the value of the number is
stored after an assignment. The variable simply makes it easier for you to access the mem-
ory location within the object. When you assign a new value to the variable, the memory
location within the object is overwritten. This assignment has no effect on the clone, which
has its own memory area. If you have a variable with a complex data type, a reference to
the assigned object is stored in the location of the variable. The object is stored on the
heap. And that reference is copied just like the value of the primitive variable. So two
objects independently hold a reference to the same object. This is not critical if you are
referencing immutable objects. If the clone and prototype reference the same String object,
and you change that String on either the clone or prototype, a new String object is created
and referenced there. The bitwise copy procedure is called shallow copy. When you clone
arrays or collections, you always get a shallow copy. The Test_Collections sample project
demonstrates the shallow copy using several examples.
This may be desired as in the joint employer example. But it may also be that you do
not want to have two shorn sheep standing in the meadow. You can only put one engine in
one car. So it makes sense to change the depth of the copy and create a deep copy. To do
this, you recursively trace all referenced objects and create copies of them.
The class Object defines the protected method clone(). Subclasses make this
method public; they override the method with super.clone(). The default
implementation of the method in Object requests memory for the object’s runtime
type. The object is then copied one bit at a time. If the object to be cloned is not of
type Cloneable, the default implementation throws an exception. You as the pro-
grammer are responsible for overriding the clone() method in a meaningful way.
In particular, this also means deciding whether to create deep or shallow copies. The
caller of the clone method you override cannot tell whether you are copying deep or
shallow. You should therefore always document your procedure in the JavaDoc so
that no uncertainties arise for the user with regard to the copying behavior.
222 17 Prototype
It is entirely up to you how you implement the clone method. The following solutions are
therefore also conceivable. The clone method of the Car class could create a new Car
object with the new operator. The number of seats and the reference to the enum are cop-
ied; a new engine (no clone here!) is created.
@Override
public Obect clone() throws CloneNotSupportedException {
var newEngine = new Engine(“Bishop Motors“, 80, 1.4);
var car = new Car(newEngine, Edition.TINY, 4);
return car;
}
You can achieve a similar effect with a CopyConstructor. Here, too, a new car instance
is created; the prototype then passes itself as an argument to the private constructor when
cloned. You can find this code in the sample project CarFactory_CopyConstructor:
@Override
public Object clone() throws CloneNotSupportedException {
return new Auto(this);
}
// … abridged
}
17.1 Cloning Objects 223
Please look at these two approaches very skeptically they are not without problems.
Assume that at some point you will have to deal with inheritance – car should be super-
class for roadster, for example. How are the sub classes cloned? Roadster might try to
request a clone with super.clone() - just as it did in the previous version. The clone
method of Car would return an object of dynamic type Car, and that cannot be cast to a
subtype, say Roadster; an appropriate cast would throw a ClassCastException.
It would be conceivable to query the dynamic type and call the corresponding
constructor:
@Override
public Object clone() throws CloneNotSupportedException {
if(car.getClass() == roadster.getClass())
return new Roadster(this);
else
return new Car(this);
}
Each new subclass of Auto would get a new if branch – that’s horror! But you have to
make sure that the clone matches the type of the subclass, because a clone should satisfy
the condition prototype.getClass() == clone.getClass(). Thus, if a
class may have subclasses, so it is not final, it must implement the clone method differ-
ently. Since a superclass can never know all subclasses, it must not be its job to determine
the runtime type of the object to be cloned. Only the default implementation of Object.
clone() can take the runtime type into account. So a class that is not final should request
the object to be returned with super.clone().
It is possible, of course, that a superclass must modify private data fields before return-
ing the clone. In the example project CarFactory_Superclass, the Roadster class inherits
from Car. In addition, you want the Roadster to get a new engine of its own when cloned.
How do you achieve this goal? In this project version, I have removed the setEngine
method. So a new car can only get a new engine in three ways: Either the constructor is
parameterized appropriately, another car object changes the motor, or the object changes
its motor itself. You won’t call a constructor. So there must be another solution. Now take
a look at the example project CarFactory_Superclass .
The client calls the clone method of the Roadster class, which in turn calls the clone
method of the superclass. In the Roadster class you will find:
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public Object clone() throws CloneNotSupportedException {
var clone = super.clone();
var carClone = (Car) clone;
var newEngine = new Engine(“Roadster Star“, 80, 1.4);
carClone.engine = newEngine;
return carClone;
}
The clone method of Car first calls the clone method of Object. It gets back an object
of dynamic type Roadster – you remember: Object.clone() returns the runtime type. This
object is cast to type Car; this is allowed by Liskov’s substitution principle – a subclass
should be able to replace its base class. Now the Car instance has access to the private data
field engine and assigns a new engine. After that, the cloned object is returned. The client
receives an object of dynamic type Roadster with a brand new engine.
How is this done in practice? In practice, it makes sense for classes from which other
classes are derived to override the clone method only protected and not implement the
interface Cloneable. Subclasses are then free to support cloning or not.
Previously, the clone method was implemented according to the signature of the Object
class: The return value is of type Object; also, the CloneNotSupportedException is propa-
gated. Let’s look at these two points in more detail.
When a method returns an object of a particular type, the client must first cast it to the
expected type. Since Java 5, covariant return types are allowed; thus, the clone method is
allowed to return a Car-object, as the following code snippet shows.
@Override
public Car clone() throws CloneNotSupportedException {
var clone = super.clone();
var carClone = (Car) clone;
var newEngine = new Engine(“Roadster Star“, 80, 1.4);
carClone.Engine = newEngine;
return carClone;
}
However, if you make this change in the Car class, you must also do so in the Roadster
subclass. There, too, the return value must be of type Car, not of type Roadster.
The other issue, the CloneNotSupportedException is passionately argued about in the
forums; the prevailing opinion in the literature is not happy about this exception. On the
one hand, there is a method clone() that is overridden by the class; on the other hand,
this very method says that it may not support cloning at all. The CloneNotSupportedException
is thrown when the default implementation of the clone() method determines that the
object to be cloned is not of type Cloneable. However, this case is only relevant if none
of the superclasses in an inheritance hierarchy implements the interface Cloneable.
17.2 A Major Project 225
However, this error can be detected and cured at compile time; it is therefore dispropor-
tionate to propagate a checked exception. For practical purposes, it is therefore a good idea
to catch the exception within the clone method and throw an AssertionError instead:
In the following section, you design a larger project that builds on prototypes.
You draw different colored circles and connect them with lines. Both circles and lines are
graphics. You can delete both circles and lines. Lines that no longer connect two graphics
after deletion are also deleted. Figure 17.1 shows the program in action.
Right-click to open the context menu and choose to either draw a line or add a circle or
select a graphic. You have different circles at your disposal: red, blue, green and so on. If
you choose to draw a line, click either a circle or a line and drag a line to the target graphic,
which can also be either a line or a circle. If you select Select a graphic, you can select
either a circle or a line. Once you have made a selection, choose “Edit…” and “Delete
selected” from the main menu. The selected graphic will then be deleted, with all affected
connections – lines – also being deleted. If you have selected a circle, you can also move it.
You can model the highway network of Germany, the metro map of Paris or whatever
with this version of the graphics editor. In further expansion stages, you could add boxes
in addition to circles and turn the framework into a small UML editor. Before I show you
the essential classes of the sample project GraphEditor_1, it’s best to open it with NetBeans
and familiarize yourself with the handling.
The program is started with the main method in the ApplStart class. This method
creates a JFrame and adds the main menu and the drawing area, an instance of the
226 17 Prototype
In the constructor, the prototype circles are also created as menu items. First, the proto-
type manager queries all prototypes. Then a separate menu item is created iteratively for
each prototype; when called, the prototype is passed to the data field nextGraphic.
public PanelCanvas(Diagram) {
// … abridged
If you have selected a circle and click on the drawing area, the event handler mouse-
Clicked is called, which requests the prototype from the data field nextGraphic,
clones it and saves the clone in the diagram.
@Override
public void mouseClicked(MouseEvent event) {
mousePosition = event.getPoint();
228 17 Prototype
if (nextGraphic != null)
try {
var newGraphic = (GraphicIF) nextGraphic.clone();
selected = newGraphic;
diagram.add(newGraphic, mousePosition);
} catch (CloneNotSupportedException ex) {
new ErrorDialog(ex);
}
else
// Search for a graphic object
// at the mouse position and select
selected = diagram.findGraphic(mousePosition);
repaint();
}
These are the essential points of the program. Everything else are gimmicks that facili-
tate the handling of the program. Please analyze the code of the program independently.
In the second version (sample project GraphEditor_2) it should be possible to clone the
whole diagram. Two canvas instances are placed on the JFrame. If you click on Clone
Diagram in the File menu item, the diagram is copied from the left side to the right side.
You can now modify both diagrams independently. Figure 17.2 shows you the interface of
the new version.
The menu item is defined in the ApplStart class and hooked into the menu. It que-
ries for the diagram at the left panel and inserts it at the right panel. The PanelCanvas
class defines the getDiagramAsClone() method in this version. First, the
ByteArrayOutputStream baos is created and passed to the ObjectOutputStream
oos. Into the baos the diagram is serialized.Then you create the ByteArrayInputStream
bais. The data of the baos is passed to this. The bais is passed to the ObjectInputStream
ois, which deserializes the serialized diagram. The resulting object is cast to a diagram
and returned. Serialization and deserialization cause the objects, including the objects they
reference, to be independent of each other.
ByteArrayInputStream bais;
ObjectInputStream ois;
try ( var baos = new ByteArrayOutputStream()) {
oos = new ObjectOutputStream(baos);
oos.writeObject(diagram);
bais =
new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
clone = (diagram) ois.readObject();
}
oos.close();
bais.close();
ois.close();
} catch (IOException ex) {
new ErrorDialog(ex);
} finally {
return clone;
}
}
230 17 Prototype
In the latest version of the program you will design your own prototypes.
A special feature of the Prototype Pattern is that you can develop and add your own proto-
types at runtime. The example project GraphEditor_3 is based on the first project version.
So you already know most of the functionality. There is a new menu item New Prototype
in the Edit menu. When you select this menu item, a color selection dialog appears. Select
a color and click Ok. A circle with the desired color is now available as a prototype in the
context menu. In the class ApplStart the action newPrototypeAction is defined,
which calls the method createPrototype() on the drawing area. Within the method
a JColorChooser is called. With the return value a new circle is created and added as pro-
totype to the context menu as well as to the prototype manager.
From the example project GraphEditor_3 you can see in Fig. 17.3 first the UML diagram
of the packages graphics and prototype, and then in Fig. 17.4 the UML diagram of the
package delegate and the actual application.
17.4 Summary
Fig. 17.3 UML diagram of the prototype pattern (sample project GraphEditor_3, packages graph-
ics and prototype)
• Each prototype must override the clone() method when using the cloneable interface.
• The method clone() is defined in Object with the visibility protected.
• The default implementation of this method results in a flat copy of the object; refer-
enced objects are not cloned.
• Collections also clone as a flat copy by default.
• Superclasses should override clone() at least in a protected manner to allow
subclasses to be implemented in a meaningful way.
232 17 Prototype
Fig. 17.4 UML diagram of the Prototype Pattern (example project GraphEditor_3, package dele-
gate and application)
The Gang of Four describes the purpose of the “Prototype” pattern as follows:
Determine the types of objects to create by using a prototypical copy, and create new objects
by copying that prototype.
Builder
18
From the set of generation patterns, you know the singleton pattern, the two factories, and
the prototype pattern. The Builder Pattern is the last pattern you use to create objects with-
out using the new operator. With the builder pattern, you have an object that is complex or
complicated to construct. This object cannot be created in one pass, but goes through an
elaborate construction process.
To get started, let’s start with a simple example. If you want to book a trip online, you can
specify a variety of search parameters. The most important parameters are probably the
time period and the number of days you want to travel. It is also interesting to know how
many people you will be traveling with and how many children (under 14) you will be
taking with you. You will certainly also want to specify how many stars your accommoda-
tion should have. And important are the rating of the accommodation and the recommen-
dation rate. Maybe you want to be on the safe side and only want to see hotels that have
already been rated by at least x guests. You save all these search parameters in the data
fields of a travel object.
Take a look at the Telescoping_Constructor_Pattern sample project. You pass the search
parameters to the constructor.
Calling this constructor is sheer horror! The main method of the ApplStart class
demonstrates this:
The problem is not only that you have to pass a lot of parameters – they are of the same
type. You will never find out what each parameter stands for without documentation.
Errors will creep in very reliably with such a call. One solution might be to overload the
constructor. If the client doesn’t require a rating or even a minimum number of ratings, it
should be allowed to omit them. The new constructor calls the one just presented with
this() and passes default values:
18.1 An Object Creates Other Objects 235
The client now creates a travel object with a slightly shorter parameter list:
Is this call more appealing or clear? No! This solution, called the Telescoping
Constructor Pattern, has made its way into the list of antipatterns. It’s never a good idea to
have to pass too many parameters to a method – even more so when they are of the same
type. Let’s look at another solution.
In Java introductions you will find the advice that data fields must be encapsulated and
may only be read and modified via getters and setters – and the version in the sample proj-
ect JavaBeans_Pattern is oriented to this. Information that cannot be dispensed with during
construction, i.e. time frame, duration, and number of travelers, is stored in final data fields
that are initialized by the constructor. The remaining information is preset with default
values and can optionally be overwritten with setters.
// … abridged
}
The client can now create an instance of the Trip class much more clearly.
The code is indeed much more speaking now. However, the other data fields are not
final and cannot be. Subsequent modification of the values is therefore allowed, even if it
may not be desired. The approach is not quite the same as the JavaBeans specification, but
merely adopts the essential principle. The JavaBeans specification requires that you always
define a default constructor. Incidentally, you will also occasionally come across opinions
in the literature that relegate the JavaBeans specification to the realm of antipatterns – for
the reasons just mentioned.
Since the toolbox of OOP does not allow a satisfactory solution, it is time for a pattern.
The Builder Pattern assumes an object whose task is limited to constructing another object.
In the version in the Builder_Pattern sample project, you now realize the travel class by
storing all information in final data fields. As with Singleton, the constructor is private, so
that an object can only be created within the class. The constructor expects an object of
type Builder as parameter.
18.1 An Object Creates Other Objects 237
The Builder type is defined as the static inner class of the trip. The data fields of the
trip are also declared here. The important data fields are initialized in the constructor, the
others get default values. For each data field, there is a setter that, contrary to language
convention, only repeats the name of the field. Inside the setter, the corresponding data
field is assigned a value; finally, the setter returns the builder instance.
// … abridged
}
The build() method then finally creates the journey object and passes the builder
object to its constructor for this purpose.
In the first step, the client creates a builder, supplies it with the relevant data – if
required – and only then has a trip object returned.
This first realization of the Builder Pattern should give you a sense that it can be useful
to have objects whose functionality is limited to creating other objects. Please don’t be
surprised that for simplicity reasons I have given LocalDate.now() for the startDate
as well as for the endDate in the first examples; in practice it is of course impracticable to
have 15 days of vacation in the time “from today to today”.
Somewhat unattractive is still the new Trip.builder in the main method. You can still
create a builder in the trip class with a static method:
If you then want to do without a separate builder variable in the main method, the fol-
lowing code (which you will also find in the example) will also work:
The purpose of the Builder Pattern is to outsource the construction process to a separate
class. This always makes sense if the construction of an object is complex or should be
replaceable. In the following example, I would like to take up the budget book from the
last chapter. To do this, open the sample project BudgetBuilder. In practice, you probably
don’t want to store the various items and categories in the source code, as I showed you
with the composite pattern in Chap. 12. You may want to read the data from an XML file
and display it in a JTree. The Builder Pattern is ideally suited for this, since a node in the
JTree cannot be created in one pass; only when it has no further subnodes can the object
creation be completed.
240 18 Builder
… abridged
</Budgetbook>
18.2 A More Complex Design Process 241
Your task will now be to turn the XML document into a TreeModel and display it in
a JTree.
I first took the last project version of the chapter about the composite pattern. In this, I
created a new package builder, where you can find the abstract class Builder.
Builder inherits from org.xml.sax.helpers.DefaultHandler and forces its
subclasses to define the startElement() and endElement() method. When an
element of the XML document is opened or closed, one of these methods is called. If the
XML data is not valid, the warning(), error(), or fatalError() methods emit
appropriate error messages. The client first lets an instance of the Builder type create an
object and then fetches the finished product from this instance; each subclass must there-
fore also offer the getProduct() method.
@Override
public abstract void startElement(String uri,
String localName, String name,
Attributes attributes);
@Override
public abstract void endElement(String uri,
String localName, String name);
@Override
public void warning(SAXParseException exception) {
System.err.println(“-> warning: “ +
exception.getMessage());
}
@Override
public void fatalError(SAXParseException exception) {
System.err.println(“-> FATAL ERROR: “ +
exception.getMessage());
}
@Override
public void error(SAXParseException exception) {
System.err.println(“-> error: “ +
exception.getMessage());
242 18 Builder
}
}
The first concrete builder you will create is the TreeModelBuilder. It has two data
fields: the root node and a list where all nodes are temporarily stored. The startEle-
ment() and endElement() methods are responsible for the actual construction pro-
cess. When an element is opened, the startElement() method first checks if the
element is called “Item”. If so, the attributes are retrieved and a new leaf is created; other-
wise, a new composite is created. If the root is null, that is, only for the very first ele-
ment, the currently created element is passed to the root data field. If the root data field
is not null, that is, starting with the second element, the new element is appended to the
last composite stored in the list as a child node. The node is then stored in the list.
@Override
public void startElement(String uri,
String localName, String name,
Attributes attributes) {
Node node;
if (name.equalsIgnoreCase(“Item“)) {
var tempDescription =
attributes.getValue(“description“);
var tempAmount = Double.
parseDouble(attributes.getValue(“amount“));
var tempRequired =
attributes.getValue(“required“).
equalsIgnoreCase(“yes“);
node = new Leaf(tempDescription, tempAmount,
tempRequired);
} else {
var tempDescription =
attributes.getValue(“description“);
node = new Composite(tempDescription);
}
if (root == null)
root = (Composite) node;
else {
var tempNode = (Composite) stack.peekLast();
tempNode.add(node);
}
18.2 A More Complex Design Process 243
stack.add(node);
}
// … abridged
}
The method endElement() is called when the element is closed. It can be limited to
removing the last element stored in the list there. The method getProduct() creates a
MyTreeModel with the data field root and returns it.
@Override
public void endElement(String uri, String localName,
String name) {
stack.pollLast();
}
@Override
public TreeModel getProduct() {
return new MyTreeModel(root);
}
The client first creates a builder instance, in this project a TreeModelBuilder. It passes
this builder to the build() method, which first reads the XML file, converts it into a
string and parses it with the builder as handler.
Budget() {
var builder = new TreeModelBuilder();
build(builder);
var treeModel = (TreeModel) builder.getProduct();
var frmMain = new JFrame(“Builder Pattern Demo“);
frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
var trvBudgetBook = new JTree(treeModel);
trvBudgetBook.setCellRenderer(
new MyTreeCellRenderer());
trvBudgetBook.setEditable(true);
trvBudgetBook.setCellEditor(new MyTreeCellEditor());
var scrTree = new JScrollPane(trvBudgetBook);
frmMain.add(scrTree);
frmMain.setSize(500, 500);
frmMain.setVisible(true);
}
public void build(Builder builder) {
244 18 Builder
Background Information
If you critically review the example, you will notice one disadvantage of the pattern:
The builder is strongly tied to the object being created. Since it queries whether an
element is called “position”, this builder is bound to the given DTD. Likewise, the
builder must have precise knowledge of the construction of leaf and composite
classes.
On the other hand, you can see the big advantage: You can pass any XML file that
conforms to the DTD to the Builder – you will always be able to generate a suitable
TreeModel from it.
Your program is now much more modularized. As a result, the routine that reads
the XML document can be replaced by a routine that obtains the XML document
from another source, such as the network. This change has no effect on the Builder.
In the next section, you will define a new builder that will bring the data into
HTML format.
The following example extends the project with a new builder. It should be possible to
generate and display the XML file as an HTML document. Follow the necessary steps in
the sample project BudgetBuilder_Ext. In the first step, you create a new builder, the
HTMLBuilder, which inherits from the Builder class. Nothing changes in the star-
tElement() and endElement() methods. The getProduct() method generates
an HTML string from the information in the root and returns it.
18.2 A More Complex Design Process 245
@Override
public String getProduct() {
html.append(“<html><body><h1 align=\“center\“>“);
html.append(root.getDescription());
html.append(“</h1>“);
html.append(“<b>“ + “Annual items:</b><br/>“);
for (var i = 0; i < root.getNumberOfChildNodes();
i++) {
var tempNode = root.getIndex(i);
if (tempNode.getClass() == Leaf.class) {
html.append(“ ”);
var item = (Leaf) tempNode;
formatLeaf(item);
html.append(“<br/>“);
} else {
html.append(“<p>“);
appendElements(tempNode, 0);
html.append(“</p>“);
}
}
html.append(“</body></html>“);
return html.toString();
}
Both methods call the formatLeaf() method, which checks an output item to see if
it was required. If not, the display text is colored red.
Background Information
To create a string in multiple steps, you could concatenate strings: String x + =
stringY;. Since strings are immutable, each concatenation creates a new string
object with the value x assigned to it. The cost is correspondingly high. Alternatively,
access a StringBuilder or a thread-safe StringBuffer and append new strings to the
previously stored text. Only when the construction is complete do you return the
finished string with StringBuilder.toString();. Here you can see the
builder pattern in practical use again.
The client creates a builder and gets the HTML string from there. This is displayed in
a JEditorPane:
Budget() {
var builder = new HTMLBuilder();
build(builder);
var html = builder.getProduct();
Fig. 18.1 UML diagram of the Builder Pattern (sample project HaushaltsBuilder_Ext)
248 18 Builder
By the way, you can also “stagger” builders if, for example, you need a very specific
sequence in the creation process of the finished object. However, you must then name the
corresponding builders individually. For example, for an SQL statement, this could be a
selectBuilder, a fromBuilder, and a whereBuilder, each of which can only return the sub-
sequent builder in its last build step. This then leads to chained builders, so-called
“NestedBuilders” and also works, for example, for the construction of composite objects.
You can find the UML diagram from the sample project HaushaltsBuilder_Ext in Fig. 18.1.
18.4 Summary
The Gang of Four describes the purpose of the “Builder” pattern as follows:
Separate the construction of a complex object from its representation, so that the same con-
struction process can produce different representations.
Visitor
19
When you work with the Visitor, another behavioral pattern, you are always dealing with
a collection of objects with different interfaces. Related operations are to be performed on
all objects, which are grouped together in their own class, the Visitor.
A car is an aggregate; it consists of wheels, an engine and the chassis. The car as well as
its components are grouped under the interface Element.
Please analyze the classes in the package visitordemo.car of the sample project
Visitor. You will find that each element has its own data fields and methods. Besides, the
methods getDescription() and accept() are prescribed in the interface. One
method will return a short description, the other will provide an interface for a Visitor
object. Take the Wheel class as an example. The wheel can store its position – for exam-
ple, front left. It can also store and return the air pressure. The accept() method is
overridden so that the visit() method is called on the Visitor object passed in, and
passing over its own instance – this.
@Override
public void accept(VisitorIF visitor) {
visitor.visit(this);
}
@Override
public String getDescription() {
return “- Wheel “ + position;
}
}
On the chassis, the wheels are mounted. When the accept method is called on it, in the
first step it calls the visit method on the Visitor and passes itself as a parameter. In the
second step, the visit method is called for each wheel.
Chassis(wheel[] wheels) {
this.wheels = wheels;
}
@Override
public void accept(VisitorIF visitor) {
visitor.visit(this);
for (var wheel : wheels)
wheel.accept(visitor);
}
@Override
public String getDescription() {
return “- Chassis“;
}
}
19.1 A Simple Example 251
The car should also still be described in the required brevity. It stores the infor-
mation whether the tank is full, whether there is enough oil and whether blinker
water (this innovative concoction is here representative for any other possible con-
sumption liquid) was refilled. There are corresponding access methods to these
attributes. The constructor installs a new engine, creates the chassis and attaches
the wheels to it.
// … abridged
public Car() {
parts.add(new Engine());
parts.add(new Chassis(new Wheel[] {
new Wheel(Position.FL),
new Wheel(Position.FR),
new Wheel(Position.RL),
new Wheel(Position.RR)
}));
}
}
The accept() method calls each component’s visit method and passes them a refer-
ence to the visitor. Then the visit method is called on the Visitor and the car passes itself
over as a parameter. I won’t print the Engine class here – it follows the same logic as the
previous classes.
The Visitor inheritance hierarchy is tightly bound to the Element classes. It must
have precise knowledge of the interfaces of the objects to be visited, as you will see
in a moment. Conversely, the element classes only need to know the Visitor interface.
@Override
public void visit(Wheel wheel) {
builder.append(wheel.getDescription()).
append(“\n“);
}
@Override
public void visit(Engine engine) {
builder.append(engine.getDescription()).
append(“\n“);
}
// … abridged
}
19.1 A Simple Example 253
The client creates an instance of the Auto class and a component Visitor. It then calls the
accept() method with the Visitor as a parameter. The resulting string is output to the
console.
// … abridged
}
}
Components:
- motor
- chassis
- Wheel front left
- Wheel front right
- Wheel rear left
- Wheel rear right
- Car (remaining components)
When you drive to the workshop because of an irregularity on your vehicle, a person there
hooks up a diagnostic device to a defined interface in the car and can shortly afterwards
say precisely whether the car has a defect, and if so, in which element. This situation is
mapped by the DiagnoseVisitor. It works very similarly to the Component Visitor. It que-
ries certain information on each element and evaluates it. There are two different proce-
dures. The car can measure by itself if the tank is full and if the oil level is correct; therefore,
it returns a boolean value with the appropriate access methods. I assume that very few cars
measure the air pressure in their tires independently, although this is technically possible.
254 19 Visitor
Here the visitor gets the current value and evaluates it. At the end it can give information
whether the car is ready to drive. Please analyze the DiagnoseVisitor independently – the
class is not complicated.
It becomes clear what I meant at the beginning by related operations: There are a number
of classes on each of which a diagnosis is to be performed. Instead of defining the diagnos-
tic methods in the respective classes, they are combined in a separate class. This has two
advantages: First, the classes are not “polluted” by code that is not part of their actual core
business. In addition, the newly defined operations can be maintained much more easily;
they are located in a single class and do not have to be maintained separately in umpteen
classes.
The classes involved do not have to have a common interface. In the “Visitor” project,
both the car and the components were subclasses of the Element interface. However, this
is not mandatory in the sense of the Visitor pattern.
A disadvantage of the Visitor Pattern is that the visited object must have a sufficiently
extensive interface to be able to query all relevant data. For example, the wheel class must
be able to provide information about air pressure, the engine must be able to name its state,
and so on. You may even need to use the Visitor Pattern to provide access to data that you
intended to encapsulate.
Another disadvantage is that you will have extensive maintenance on all concrete
Visitor classes when a new element is inserted. Imagine that you additionally define the
class Brake – the interface Visitor must declare a corresponding visit method, which must
be overridden by all concrete Visitor classes. The Visitor pattern is always best used when
new operations are needed rather than changing the object structure more frequently.
The Visitor Pattern is an excellent example of the Open-Closed Principle. You have
a set of classes that are closed against change. But they leave the door ajar for exten-
sions by providing an interface that allows other objects to define new behavior.
One last aspect should be addressed: the type of iteration. In the project you have just
worked on, you will find an internal driver – you call the accept() method on the Car
object, which ensures that iteration is performed over all components. You could also con-
ceivably define an external iterator. The solution with an internal iterator does not have to
be the one you find practical.
19.2 Visitor – The UML Diagram 255
Fig. 19.1 UML diagram of the Visitor pattern (example project Visitor)
You can see the UML diagram from the Visitor sample project in Fig. 19.1.
256 19 Visitor
19.3 Summary
The Gang of Four describes the purpose of the “Visitor” pattern as follows:
The starting point is to be any object A whose state you want to store externally in order
to be able to restore it later. How can the state be determined and stored? First, it is obvious
to take an object B that reads the data from A and stores it. But how could the storing
object B get at the data of the object A to be stored?
Now another object could read and store the state. But why won’t you pursue this
approach? The data fields are publicly viewable and modifiable, which goes against the
principle of data encapsulation, information hiding.
// … abridged
}
Now the data is encapsulated, but nothing has changed compared to the approach with
the public fields – anyone can still read and write to the data, albeit via the detour of the
access methods.
Conceivably, you could set the fields – or the access methods – to default visibility (package-
private); then only classes in the same package could access the data. Consequently, an
object that is to store the state of another object must be defined in the same package. But
even this approach is not optimal. Packages should help to group classes into meaningful
units. For example, all classes that display data on the screen go into one package, while
data that describes the data model is defined in another package. In my opinion, it would be
excessive to create a package just to store a class and its data store in it.
The previous suggestions were not convincing – it seems like a bad idea to try to read
the data. So, take a different approach: the object whose state is to be stored is itself made
responsible for creating its data store.
20.2 A Possible Realization 259
Before I introduce you to the project, I would like to define a few terms. The object whose
state is to be stored is the originator; in the German translation of the GoF book it is also
called “Urheber”. The object that stores the state of another object is the memento. Finally,
the caretaker maintains a list of mementos. The example project Memento_Simple imple-
ments the pattern in a very clear way. The caretaker is the class Stack. It stores the dif-
ferent memento objects. As stack I didn’t take the Java implementation, but developed my
own class. The memento is defined as an empty marker interface.
You want to save the state of a car object. It has the attributes speed and current-
FuelConsumption. The speed can be influenced; there are corresponding access meth-
ods for this. The fuel consumption can only be influenced indirectly via the speed.
Now I am looking for a way to be able to store all the data fields. For this, I create an
inner class AutoMemento which is suitable to copy the data fields. Every time you create
an instance of the AutoMemento class, the data fields of the AutoObject are copied.
260 20 Memento
// … abridged
}
You pass the responsibility of creating a memento to the car class. There you request a
memento; moreover, the car is also responsible for restoring an old state from a given memento.
The client creates a stack, the caretaker, and an auto object. Then it performs some
operations that affect the speed and thus the fuel consumption, and saves the state in a
memento after each change. The memento is placed on the stack; any change can now be
undone. The main method of the ApplStart class demonstrates this procedure. Please
analyze the code on your own.
How do you evaluate this solution? On the one hand, the attributes of neither the
originator nor the memento can be read – so they are very consistently encapsulated.
However, you buy the encapsulation at the price that the class itself is responsible for
creating and restoring the memento; the code is “polluted” in this respect. If this
solution appeals to you, please note that the example is extremely simple. The attri-
butes are of the primitive type int. If you have mutable objects, they must be cloned –
the problem of copied references, which you learned about in the Prototype chapter
on cloning, is encountered again here.
20.3 A Major Project 261
In the next example we will use the graphic editor of the Prototype Pattern again.
In the second expansion stage of the GraphicEditor in the chapter about the Prototype
Pattern, you cloned a diagram by serializing and deserializing it. Exactly this procedure
can also be interesting for the Memento. For the following example project Memento, I
took the GraphEditor_2 as a template and modified it. The advantage now is that in
the class PanelCanvas the method getMemento() is defined – it creates and returns
a cloned diagram as a deep copy, the Memento.
The setDiagram() method takes a Diagram object and fills the drawing area with
its data.
In the main method of the ApplStart class, the undoAction action is defined,
which requests the last diagram from the caretaker and passes it to the drawing area. This
action is passed to a MenuItem. The caretaker is an instance of the Stack class, which
you know from the last example. You pass the same instance to the drawing area.
You could trigger most actions via mouse clicks: Moving circles, creating lines or cir-
cles, and selecting graphic elements. You deleted graphic elements via the menu. All the
methods you called this way were defined in the PanelCanvas class – either as a standalone
method or as an EventListener. These methods are supplemented so that a snapshot is cre-
ated and saved at the same time as the defined action.
Fig. 20.1 UML diagram of the Memento Pattern (example project Memento)
In this realization, you store an object of type Diagram in the caretaker. Alternatively,
you could have stored the serialized object in the caretaker.
In Fig. 20.1, I show you only some of the classes involved in the Memento sample project.
I have not shown the graphics and prototype packages again here. You can find their rep-
resentation in the UML diagram for prototype in Sect. 17.3.
264 20 Memento
20.5 Summary
The Gang of Four describes the purpose of the pattern “Memento” as follows:
Capture and externalize the internal state of an object without violating its encapsulation, so
that the object can later be restored to that state.
Facade
21
The facade belongs to the structure patterns; it describes how you can access a compli-
cated or complex subsystem in an uncomplicated way.
Imagine that you have a complex or complicated system – for example, an SLR camera.
When you take a portrait, you want the depth of field to cover only a small area. You open
the aperture as wide as possible, so choose a small f-number. This allows more light to fall
on the film or the processor. To prevent the image from becoming too bright, you need to
reduce the exposure time.
Maybe you don’t want to take a portrait later, but a landscape. There, the depth of field
should be as large as possible; you select a large f-stop number and thereby close the aper-
ture. Now there is less light reaching the processor, so you have to increase the exposure
time. However, the exposure time cannot be increased indefinitely; experience shows that
a picture can only be taken handheld if the exposure time is shorter than 1/focal length. If
the exposure time is longer, you run the risk of blurring the image. So you need to increase
the film speed, the ISO number.
Does that sound complicated? I think it is! And most camera manufacturers see it the
same way. Modern compact cameras, but also (digital) SLR cameras, come with scene
programs. All you have to do is tell the camera: “I want to take a portrait!” or: “I want to
take landscapes!”. The camera automatically sets the aperture and exposure time so that
the result is optimal.
This actually explains the principle of the facade: You have a complicated or complex
system. To make it easier for you to access the system, a facade is created. As a user or
photographer, you no longer need to concern yourself with the details. It should be enough
that you tell the facade – the subject program “Portrait” – what you want to have. The “how”
is realized by the facade. You will still have access to the individual components of the system:
you do not have to use the facade, you can still set the aperture and exposure time manually.
In the following section, after this short excursion, you will again be introduced to an
example from IT.
Take a look at the example project facade. It consists of the packages demo1 to demo4 and
the package trip. In this example, you will create a journey. For a trip, you must first take
the train to the airport. From there, you fly to your vacation destination. After you arrive at
the far airport, the transfer service will take you to the hotel. At the hotel, you will be pro-
vided with either all-inclusive or half board or breakfast only. Optionally, you can take a
rental car, which must be insured and refueled. In the package trip all necessary classes
can be found. You can see how this project looks in the development environment in
Fig. 21.1.
If a client wants to create a trip, it must create all components. Look at the client code
in the package demo1.
If the customer also needs a rental car, they must also first create a rental car object and
then call the insure() and refuel() methods on it. So on the one hand, you have
many components that you have to serve. On the other hand, you have different configura-
tion options – in one case it is sufficient to create objects, in the other case you have to call
methods on the objects in addition. Does this look like a complex system to you?
21.2 The Facade in a Java Example 267
// … abridged
System.out.println("\nAnother Client:");
railAndFly = new RailAndFly();
transfer = new Transfer();
hotel = new Hotel();
halfBoard = new HalfBoard();
var rentalCar = new RentalCar();
rentalCar.insure();
rentalCar.refuel();
}
}
268 21 Facade
You must have noticed that the flight was forgotten in the upper listing. But maybe you
know the problem from practice: Errors in extensive configurations can only be found
after an even more extensive test procedure. So the code from above is error-prone – in the
next section you will learn a solution to this dilemma.
You introduce a facade whose only task is to provide a simplified handling for the client
on the one hand, but on the other hand to master the complex interrelationships of the
system. In the package demo2, you will find the class TravelAgencyFacade, which
offers the method bookTrip(). You give this method a truth value as a parameter that
specifies whether a car should be rented.
The client class in the package demo2 can now call the method very easily.
System.out.println("\nAnother Client:");
new TravelAgencyFacade().bookTrip(true);
} }
It is now much easier for the client to book a trip. Let me point out one thing: In
practice, you will just call the class TravelAgency. I only added the addition
TravelAgencyFacade for clarification.
21.2 The Facade in a Java Example 269
The term “system” is important for the facade: They provide a simplified access to a sub-
system. The term system or subsystem is to be interpreted broadly. It can mean an arbi-
trarily large unit; but it can also mean access to a single class. In the package demo3, I have
considered the car rental as a separate system and moved it to a separate package. I also
defined the Car Rental facade, which has a single static method that can be used to rent a
car: Access to this (sub)system is now only possible through the facade.
The client can either rent a car from the travel agency or directly from the car rental
company. By the way, in the implementation I’ve presented to you here, it’s not a prob-
lem that the client still accesses the individual elements of the subsystem. The facade
does not restrict, nor does it introduce any new logic – it merely simplifies access to a
subsystem.
An alternative procedure is demonstrated by the package demo4. Here there is the
subsystem (subpackage) excursion. This system is represented in a simplified way by the
class Excursion. The class and its methods are package-private, so that access is only
possible through the facade. Please analyze demo4 from this point of view. It is also con-
ceivable that the facade not only mediates between client and subsystem, but that it
defines its own logic.
270 21 Facade
In the next section, let’s look at where the facade can be found in the Java class library.
I would like to show you an example from the Java class library. You have created a graphi-
cal user interface and want to display a message to the user. You could now design a
JDialog that has a BorderLayout. On BorderLayout.WEST, you place an icon
that displays a warning exclamation point. On BorderLayout.CENTER, display the mes-
sage. BorderLayout.SOUTH contains a JButton labeled “Ok.” When the user clicks on
it, the EventListener will cause the JDialog to close.
Alternatively, you could make do with the facade of the JOptionPane class, which
brings various static methods that you can pass different parameters to in order to config-
ure the dialog. Consider the following lines of code.
When you run this code, you will be presented with a message, as shown in Fig. 21.2.
The facade requires a large number of classes that it calls. To give you an idea of the
large number of classes involved, I am printing an overview in Fig. 21.3, which I have
borrowed (and slightly adapted in layout) from Philipp Hauer.1
I think this example makes the meaning of the phrase “simplified access to a subsys-
tem” pretty clear.
1
https://www.philipphauer.de/study/se/design-pattern/facade.php
21.4 The “Law of Demeter” 271
Fig. 21.3 Extract from the API Doc of the JOptionPane Class
In Chap. 2 I showed you some design principles. Now I will address another design prin-
ciple. It is about the “Law of Demeter”,2 which says – in short – that objects should limit
their communication to close friends. The facade shows you one way to make this happen
as easily as possible. So what exactly does Demeter’s Law say? Objects should only com-
municate with close friends. Close friends are:
• Attributes and methods of its own object, that is, everything called this,
• Methods of objects passed as parameters,
• Methods of objects that the method itself creates,
• Global objects.
If you look at the travel example from above, you can see that the client only needs to
access one friend – the facade. The facade gives the client the option of not having to deal
with strangers, i.e. additional classes. The facade is an illustrative example of the realiza-
tion of the principle. I found a detailed description of the LoD on Matthias Kleine’s blog.
If you want to look further into object-oriented design principles and principles of soft-
ware engineering, this page is a good starting point for your research:
http://prinzipien-d er-s oftwaretechnik.blogspot.com/2013/06/das-g esetz-v on-
demeter.html
You can also find the (german) text as “LoD.pdf” in the directory of sample projects for
this chapter.
2
Demeter in Greek mythology is the mother of Persephone and the unwilling mother-in-law of
Hades. She provides seasons, fertility and growth.
272 21 Facade
In the chapter on object-oriented design patterns, I felt it was important to say that
you, the programmer, are not there to satisfy the laws. Rather, the laws are there for
you-they are there to make your job easier. Therefore, even with Demeter’s law,
nulla regula sine exceptione, or No Rule Without Exception, applies. You may break
the law if you are clear about it and can justify the breach. If you want to print some
text on standard output, keep coding System.out.println(). And if you want
to know the name of a class, the statement myObject.getClass(). get-
Name() is still allowed. However, be careful whenever you access objects that are
not part of Java’s standard class library.
I would like to offer you an introduction to design patterns with my book. Therefore, I
limit myself to a few design principles. However, I hope that I can motivate you to deal
with the topic in your own research.
Figure 21.4 shows the UML diagram for the example project Facade, package demo4.
21.6 Summary
• The initial situation is a complicated system that you want to work with.
• Access to the system is simplified by a facade.
• The client accesses the facade and does not necessarily know the details of the system.
• The facade can either contain business logic or simply forward requests to the appropri-
ate system object.
• The system becomes interchangeable.
• Dependencies between systems are simplified or dissolved.
• Optionally, a client can still access the system.
• The Demeter law is implemented.
21.7 Description of Purpose 273
Fig. 21.4 UML diagram of the Facade Pattern (example project facade, package demo_4)
The Gang of Four describes the purpose of the “Facade” pattern as follows:
Provide a unified interface to a set of interfaces of a subsystem. The facade class defines an
abstract interface that simplifies the use of the subsystem.
Adapter
22
You are going to London and want to plug your hairdryer into the socket – since the
German plug does not fit into the British socket, you need an adapter. I present you the
pattern on the level of single classes. In fact, it can be scaled – the principle is the same if
you adapt whole subsystems.
Let’s generalize the plug example. You have two systems – the hairdryer and the plug
socket – which have to work together. The “must” means that you have no other option in
London than to dock with the UK socket. In IT, “must” can mean that you want to use a
system or class that represents something unique or highly complex, such as a premium or
deadline calculation in the insurance industry. A complex adapter is also JDBC, for exam-
ple. The interaction of the systems that “must” work together is only disturbed by the fact
that the client expects a different interface than the system to be used declares. The adapter
has the task of mediating between the two systems. The GoF describes the adapter in two
ways: object-based and class-based. Both designs are discussed in this chapter.
What is the task to be solved in this chapter? Take a look at the Adapter_ClassBased
sample project. You have purchased a third-party library that sorts numbers using a blazing
fast algorithm.
All you know about this library is that you need to pass an integer list as a parameter
and get back a sorted integer list. However, your software is programmed to expect a sorter
object with the following interface.
The source code of the third-party library is not available to you, so you cannot make
any changes to it. Your own application is an established tested procedure that has already
been delivered to many customers – it must not be changed under any circumstances. So
you have to design an adapter that mediates between the foreign library and your applica-
tion. What does such an adapter look like? The class-based approach is to let the adapter
inherit from the foreign library. At the same time, the adapter takes on the role of a sorter
object, that is, it implements the expected interface.
@Override
public int[] sort(int[] numbers) {
// … we'll deal with later
}
}
The adapter’s sort method converts the array of numbers into a list and calls the correct
method on the foreign library. So the adapter pretends to be a sorter, but is actually a sub-
class of the foreign library. Now here is the entire code of the adapter:
@Override
public int[] sort(int[] numbers) {
int z = new int[numbers.length];
List<Integer> numberList = new ArrayList<>();
for (var number : numbers)
numberList.add(number);
List<Integer> sortedList = sort(numberList);
for (var i = 0; i < sortedList.size(); i++)
z[i] = sortedList.get(i);
return z;
}
}
The GoF describes the class-based approach using multiple inheritance (in C++). Since
Java does not know multiple inheritance, you have to make the compromise that you
declare the expected interface by an interface.
If the class-based design has already solved the problem, we could actually close the chap-
ter, right? There are two reasons against being satisfied too quickly. The first reason is that
one design principle is to prefer composition to inheritance. The second reason is quite
trivial – the provider of the third-party library has marked the class as final in an update.
The adapter of the previous solution can now no longer be used. Your only option is to
program an object-based adapter. How do you do this? You create an attribute that holds a
reference to an object in the foreign library. The sort method calls the method sort() on
this attribute. You can find this variant in the Adapter_ObjectBased sample project.
@Override
public int[] sort(int[] numbers) {
List<Integer> numberList = new ArrayList<>();
for (var number : numbers)
numberList.add(number);
var sortedList =
externalProduct.sort(numberList);
for (var i = 0; i < sortedList.size(); i++)
numbers[i] = sortedList.get(i);
return pay;
}
}
You are now familiar with both approaches – object-based and class-based. In the fol-
lowing section, you will look at the advantages and disadvantages of both approaches.
Please look again at the clients of the two approaches. You can see that the client knows
the target interface and the adapter. Everything beyond the adapter is not visible to the cli-
ent. Therefore, there may be another class or an entire system behind the client. Since the
client does not know the system behind the adapter, this can be replaced without any prob-
lems. The only important thing is that the adapter is implemented correctly. In Sect. 21.4,
you looked at the Law of Demeter. The adapter pattern also helps you to develop systems
where classes communicate only with close friends.
In this context, let me distinguish the adapter from the facade. In both cases, you have
a system that a client wants to access. It does not access this system directly, but via
another abstraction. The main difference is the target direction. In the case of the facade,
the goal was to facilitate access to a complex or complicated system. The adapter has the
task of enabling two systems to communicate with each other in the first place. This brings
up another obvious difference: who creates the abstraction? In the case of the facade, it
was clear that the provider of the system must create a facade; it provides simplified han-
dling. With the adapter, things are usually different. The vendor of a system invests a lot of
time and effort in creating his software. He develops an interface to which the client can
direct its requests. With that, his job is done. If the client needs another interface, he has to
create it and define an adapter.
An adapter is usually a class that has been written and optimized for a single use case.
The adapter I developed in the example above is difficult to reuse, which is sometimes
described as a disadvantage in the literature. You will undoubtedly have to consider
22.5 A Labeling Fraud 279
whether it is better to refactor one of the interfaces in such cases. However, you cannot
rework the interface if the adapter allows you to access the Internet or a database (JDBC).
If you choose the class-based approach, the adapter can easily override methods of the
classes to be adapted. However, it is then bound to a class because it has itself become a
subclass of the class to be adapted. A new adapter must be developed for each subclass of
the classes to be adapted. What about the object-based approach? In Sect. 2.4, I touched
on Liskov’s substitution principle, which states that a subclass should behave exactly like
its base class; in other words, a subclass must be able to represent its base class. If an
inheritance hierarchy takes this principle into account, it is possible for an object-based
adapter to be used not only for a class but also for its subclasses.
Background Information
Did you know that the adapter pattern can already be traced back to the Brothers
Grimm? Surely you know the fairy tale Little Red Riding Hood. The wolf disguises
himself as grandmother; Little Red Riding Hood sees an object lying in bed that
corresponds to the interface grandmother, and at first does not suspect anything.
Later, however, it realizes that a class-based WolfAdapter is hidden behind the inter-
face – otherwise it would never have come to the legendary quote: “Grandmother,
why do you have such a horribly big mouth?” So the adapter is obviously poorly
implemented and throws a WolfException.
The fairy tale can teach us that it’s always a bad idea for people to concoct their
own solutions without checking with IT first.
You will find a large number of classes in the class library that have “adapter” in their
name. In Swing, for example, there are MouseAdapter, FocusAdapter, etc. The
MouseAdapter class implements the MouseListener, MouseMotionListener,
and MouseWheelListener interfaces. All methods are overridden empty, so you can
define a MouseListener without having to override all methods yourself. If you want
to override a method from the MouseMotionListener interface, expand the
MouseAdapter and specifically override only that one method.
Is this an adapter in the sense of the Adapter Pattern? No – the MouseAdapter class
does not bring you any added value, nor does it mediate between two systems. It does,
however, make it easier for you to deal with the interfaces mentioned. Simplified access to
a subsystem was the hallmark of the facade. So the MouseAdapter class should be
called MouseFacade. So not everywhere that says adapter on it is adapter in it.
280 22 Adapter
Figures 22.1 and 22.2 show a comparison of the UML diagrams from the two sample
projects Adapter_Class-Based and Adapter_Object-Based.
22.7 Summary
Fig. 22.1 UML diagram of the adapter pattern (example project Adapter_ClassBased)
22.8 Description of Purpose 281
Fig. 22.2 UML diagram of the adapter pattern (example project Adapter_Object-based)
The Gang of Four describes the purpose of the “Adapter” pattern as follows:
Adapt the interface of a class to another interface expected by its clients. The adapter pattern
lets classes work together that would otherwise be unable to do so because of incompatible
interfaces.
Proxy
23
The proxy pattern I’ll show you in this chapter is a structure pattern and has similarities to
the facade and adapter. In all three patterns, you don’t access an object directly, but through
a third. Remember – the facade allowed you to deal with a complicated interface. With the
adapter, the third object made it possible for two others to interact in the first place. And
what will that be like with the proxy? With the proxy, you’re also encapsulating access to
an object. But why would you want to do that? Four reasons might be:
1. Virtual Proxy: You need a placeholder for a certain duration to create a large object.
2. Security Proxy: You want to block or at least control access to the object.
3. Smart Reference: You want to add functionality to the object; for example, you want to
check whether the object is locked before actually accessing it.
4. Remote Proxy: The desired object is not in the same address space.
These four application areas are the most common; they are discussed by the GoF. However,
you will find other proxies in the literature: the firewall proxy, the synchronization proxy,
and many more. This chapter is one of the longer ones in the book. So, get comfortable,
and let’s get started.
The Virtual Proxy should only be addressed here theoretically for the sake of complete-
ness. Imagine you create a software with which photos can be displayed. For example,
there should be 100 pictures in a certain folder. With the chosen setting 25 pictures fit on
one screen page. When you open the folder, the first thing to do is to register all the images,
determine their size, create the previews and draw them. If the photos are quite large, this
process may well take a while. Now it would be very unsatisfactory for the user if your
program freezes. It is much more comfortable if the preview is generated only from the
pictures that are currently displayed on the screen. At first the screen shows 25 frames as
placeholders for the later previews. Gradually, these placeholders, the proxies, are replaced
by the actual preview images. You proceed accordingly with the following screens. You
create frames as placeholders and when the user scrolls the frames into the visible area of
the screen, the previews are created.
It can be useful to prevent write access to an object. As an example, consider the unmod-
ifiableList() method of the Collections class in the Java class library. You pass
a List object to the method and get back a read-only list. The method wraps your list in an
object of the UnmodifiableList inner class. The UnmodifiableList extends the
UnmodifiableCollection class, which is also an inner class. Now, when you want to
access the actual database, you don’t access the original list, you access the UnmodifiableList
that surrounds it. The UnmodifiableList forwards requests to the original database – write
accesses excluded. Take a look at the Unmodifiable sample project; there I extracted the
relevant code portions of the Collections class from the class library into the file unmodifi-
able.java. This file is by no means complete in this excerpt and will therefore not compile.
But that’s not what it’s intended for, it’s just a reference for you. You can of course alter-
natively look directly in the source code of the class library, if you have also downloaded
or linked it.
class UnmodifiableList<E>
extends UnmodifiableCollection<E>
implements List<E> {
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {
super(list);
this.list = list;
}
@Override
public E get(int index) {
return list.get(index);
}
@Override
23.2 Security Proxy 285
For the test, a client creates an ArrayList with various strings. Then he lets the
Collections class return a read-only list. This list is then no longer an instance of the
ArrayList class. Rather, it is an object of the UnmodifiableRandomAccessList
class. As expected, an UnsupportedOperationException is thrown when trying
to add another string.
java.util.ArrayList
java.util.Collections$UnmodifiableRandomAccessList
Exception in thread “main“
java.lang.UnsupportedOperationException
at java.base/java.util.Collections$
UnmodifiableCollection.add(Collections.java:1060)
at Test.main(Test.java:43)
C:\...\run.xml:111: The following error occurred while executing
this line:
C:\...\run.xml:68: Java returned: 1
The Smart Reference adds more functionality to the represented class, for example to
control it. In the first step, I’ll show you a version that has nothing to do with the proxy
(yet). It should only introduce the project to you.
The example in this section goes back to the Adapter Pattern example. You can find it in
the sample project Proxy_1: There is a third-party library that provides an algorithm that
is quite unique. The super-strict-secret algorithm sorts a list in a highly efficient way.
However, your client internally works with an int array. To allow the client to work with
the foreign library, an intermediate object provides a method to convert an int array to an
integer list. The modified database can be passed to the foreign library for sorting. Finally,
the foreign library provides a method to convert the sorted list back to an array. The inter-
face Sorter declares these methods.
@Override
public List<Integer> convertToList(int... numbers) {
// … abridged
}
@Override
23.3 Smart Reference 287
@Override
public List<Integer> sort(List<Integer> numberList) {
// … abridged
}
}
The client defines a certain number of unsorted random numbers with the method
createArray(). Then it creates an instance of the class SorterHelper, lets it
convert the array with the database into a list, sorts this list, converts it back into an array
and processes the array further.
The user asks whether it is worthwhile to convert the client software so that it works
internally with a list. This would eliminate the need for conversion. You want to find out if
the time it takes to convert the data is significant enough to make refactoring worthwhile.
To do this, you measure time for conversions.
To perform timing, develop a class that implements the same methods as the original
SorterHelper class. The proxy can reference an object of type Sorter. The sort()
method is used as an example to describe the procedure. The method first stores the current
timestamp, executes the actual method on the sorter instance, and then measures the time
again. The difference between the two timestamps is determined and output formatted on
the console. You can find this code in the sample project Proxy_2.
288 23 Proxy
SorterTimeProxy(Sorter sorter) {
this.sorter = sorter;
}
@Override
public List<Integer> convertToList(int... numbers) {
// … abridged
}
@Override
public int[] convertToArray(List<Integer> numberList){
// … abridged
}
@Override
public List<Integer> sort(List<Integer> numberList) {
var start = Instant.now();
numberList = sorter.sort(numberList);
var end = Instant.now();
var duration = Duration.between(start, end);
var time = duration.toMillis();
System.out.println(“sort: “ +
dateFormat.format(time));
return numberList;
}
}
As in the previous project, the client creates an array of unsorted random numbers. It then
creates an instance of the classes SorterHelper and SorterTimeProxy. You pass
the instance of the SorterHelper class to the constructor of the SorterProxy class. You
pass the commands for converting and sorting the database to the proxy.
23.3 Smart Reference 289
Each method that is called executes its own behavior on the one hand, but also triggers
the desired behavior of the target object on the other hand. Through the upstream proxy
you measure the time and get the sorted database. The following text is output on the
console.
convertToList: 01:00:00:137
sort: 01:00:01:979
convertToArray: 01:00:00:087
Your client now wishes to be able to log the algorithm. So, you need to write a second
proxy. And now it becomes clear why it makes sense to have the proxy implement the
same interface as the SorterHelper?
You already sense that the new proxy, which you find in the sample project Proxy_3,
also has to take the role Sorter. And just like the old proxy, it also holds a reference to
another sorter object. Using the sort() method as an example, we will show that the
proxy first performs its own task – logging – and then calls the desired method of the tar-
get object.
SorterLogProxy(Sorter sorter) {
this.sorter = sorter;
}
@Override
290 23 Proxy
@Override
public int[] convertToArray(List<Integer> numberList){
// … abridged
}
@Override
public List<Integer> sort(List<Integer> numberList) {
// Pre-Invoke
var strNumber = Long.toString(numberList.size());
print(“Sorts a list of ” + strNumber +
“ numbers“);
// Invoke and return
return sorter.sort(numberList);
}
The client determines which class is passed to the constructor – this can either be an
instance of the class SorterHelper or an instance of the class SorterTimeProxy. So, the cli-
ent determines whether it needs one, none or multiple proxies.
When you run the code, the following text is output to the console:
The client can switch the proxy before the actual call of the target object and thus con-
trol or direct the access.
When you analyze the code of the proxy classes, you notice that the pre-invoke and post-
invoke in each method are the same or very similar. Code duplicates are usually a sign of
poor design. There is another drawback to this: The proxy classes fully implement the
interfaces of the target classes. If you insert a new method in the interface Sorter, all proxy
classes must be adapted and implement this new method.
The goal of this section is to use Reflection to isolate common pieces of code and have
the proxy classes automatically get generated. In other words, extract the behavior and let
it generate the proxy classes at runtime. Sounds crazy? It is! Let’s take it one step at a time.
All methods are queried from the Class object and their names are output to the console:
sort
convertToArray
convertToList
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
// … abridged
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
var start = Instant.now();
var result = method.invoke(object, args);
var end = Instant.now();
var duration = Duration.between(start, end);
var millis = duration.toMillis();
var methodName = method.getName();
System.out.println(methodName + “: “ +
dateFormat.format(millis));
if (methodName.equals(“sort“))
calls++;
return result;
}
}
What’s missing now is the actual proxy class – and that’s generated at runtime, which
I’ll show in the following section.
That’s it! When you run the client’s main method, your program behaves exactly as if
you had generated the proxy yourself.
If you’ve never worked with the Reflection API before, you may find the Dynamic
Proxy a bit confusing. Perhaps the best way to understand the principle is to realize
that the InvocationHandler in the invoke() method describes the behavior of all
methods in the target interface. The proxy, which is the instance of the class that is
first created at runtime, determines all methods from the interface. Each method is
equipped with the same behavior, namely a forwarding of the request to the
InvocationHandler: handler.invoke(); Analyze the sample project. Be sure to
also read the API documentation for the Proxy class and the InvocationHandler
interface. There you will find important information about working with
Dynamic Proxy.
You can also find the sample project ProxyDynamicTable. In this project the result
array is displayed in a table. For this purpose, I have inserted the lines printed here, among
others, in the main method of the Client class:
On the console, all accesses to the methods of the TableModel interface are now
logged and evaluated. To produce measurable results, each method call is randomly slowed
down. What I want to show you with this is that the InvocationHandler can be reused. It is
only bound to an interface by the dynamically created proxy class.
Each Java program is executed in its own virtual machine. In this VM, instances of all
required classes are created. These objects swim around in the VM like fish in an aquar-
ium. When an object needs information from another object or wants to use its services, it
sends a message to that object. Just like a fish in an aquarium can only communicate with
a fish from the same aquarium, objects only know about other objects on the same
VM. With RMI, you have a way to look beyond the edge of the aquarium.
In the project I will present in this section, you will program a client that wants to know
what value Pi has. There is a server that knows this value and announces it on request. This
project is a bit simplified and does not cover all aspects of the technology behind it,
RMI. But it shows the relation to the proxy pattern.
A client does not communicate directly with the server, but through a proxy, called a
stub on the client side. To the client, it looks like the proxy is the server object. In fact,
however, the proxy only pretends to be the server object; it packages the client’s request
into a data stream and sends it to the server. The server unpacks the request and sends it to
the actual object. The way it works in reverse is the same. The server object sends its
response to a proxy, called a skeleton on the server side. The skeleton packages the
response into a data stream and sends it back to the client over the network. The data
stream relies on serialization; therefore, all parameters and return values must be serializ-
able or a primitive data type.
The following project recreates this proxy access on a small level. You can find the code
in the subdirectory RMI_Test, and this time for once not as a NetBeans project. How to get
the whole thing working, I explain below.
Let us first program an RMI server. The server is to consist of two classes and one inter-
face. The PiImpl class provides three methods. The getState() method returns that
the instance is alive. The return value of the other method – method getPi() - is the
number Pi. Finally, the getCounter() method tells us how many times the getPi()
296 23 Proxy
method has been called. The PiIF interface declares the getPi() and getCounter()
methods, which are implemented by PiImpl. The class ServerStart creates an
object of the class PiImpl, announces it as RMIServer and queries the state of the server
in an endless loop.
import java.rmi.Remote;
import java.rmi.RemoteException;
@Override
public int getCounter() {
return counter;
}
@Override
public double getPi() {
counter++;
return 3.1415…;
}
Please note that only objects within the same VM can call the getState() method.
The methods getCounter() and getPi() can also be called by objects outside their
own VM – the interface Remote ensures this.
// … abridged
}
Finally, the main method enters an infinite loop that queries every 5 s to see if the server
is still active.
This already describes the server classes. Let’s take a look at what the client looks like.
298 23 Proxy
The start class creates an instance of the Calculator class and queries the number Pi there;
the returned value is output to the console. Afterwards, the calculator is instructed to out-
put to the console how often Pi has already been queried.
From the RMI point of view, the constructor of the calculator is interesting. It calls the
lookup() method, which returns a reference to the stub object from the registry.
Naming.lookup() is something like looking into the server’s registry. Returned is the
stub of the service, which is cast to the correct interface.
CalculationEngine() {
pi = lookup();
}
On the stub, the client can now call the designated methods as if they were local.
You now know all the code – let’s look at how to make the project work in the following
paragraph.
You will find all java files in the subdirectory RMI_Test as mentioned before. Copy
them locally to your computer. Open a console window and change to this directory.
Translate the source files with javac *.java. Now call the RMI registry: rmireg-
istry. Please wait a few seconds and if necessary confirm a Windows share request
dialog for network access. Now you can start the server in a new prompt with java
ServerStart. Again, wait (and confirm another network share request if necessary)
until you see the message “Server is alive” in the output, which is repeated every few
seconds. If you now call the client from a third console with java ClientStart,
the value of Pi and the number of answered requests for Pi since the last server start will
be printed on the console.
If you start the server too soon after calling rmiregistry, you will most likely get an
exception at first, but this will be followed by the “server is alive” messages after com-
munication is established. On the other hand, if you start the client too early, it will abort
with an exception and a value of 0.0 for Pi. You will then have to restart it and should get
the correct information from the server.
Both the client and the server object communicate with their proxies – stub and skele-
ton – without knowing it. The proxy objects are responsible for transporting the data
streams across the network to the other virtual machine.
The sequence of communication is shown in the sequence diagram in Fig. 23.1.
300 23 Proxy
Figure 23.2 shows the UML diagram from the sample project ProxyDynamicTable.
23.6 Summary
• With a proxy, you do not access an object directly, but via its proxy.
• The main application areas are Remote Proxy, Security Proxy, Smart Reference and
Virtual Proxy; however, there are other areas as well.
• Virtual Proxy: You do not load all images in an album, but only those that are currently
to be displayed on the screen.
• Security Proxy: The actual target object is encapsulated in another object that – as in
the UnmodifiableList example – prevents write access.
• Smart Reference: The function of an object is extended with the motivation to log or
extend accesses.
–– The proxy implements the interface of the target object.
–– It is placed in front of the actual object.
–– The client calls the desired method.
–– The call is first processed by the proxy.
–– The call is then forwarded to the actual target object.
–– The procedure is inflexible when the interface is extended.
–– Dynamic Proxy is a relief for the programmer since Java 5:
–– The InvocationHandler defines the behavior of all methods of an interface in a gen-
eral way.
–– The proxy class is created dynamically at runtime.
23.7 Description of Purpose 301
Fig. 23.2 UML diagram of the proxy pattern (example project ProxyDynamicTable)
• Remote Proxy: allows access to objects outside the own address space
–– A client wants to access a server object outside its own address space.
–– The client gets a stub from the server to which it sends its request.
–– The stub – the client proxy – forwards the request over the network to the server.
–– The server proxy – the skeleton – passes the request to the server object.
–– The server object sends the return value to the skeleton.
–– The skeleton sends the response to the stub, which sends the response to the request-
ing local object.
–– Neither client nor server object knows that they are sending their messages to
proxies.
The Gang of Four describes the purpose of the “Proxy” pattern as follows:
The Decorator pattern – another structural pattern – adds responsibilities to objects. In the
last chapter, you saw how the proxy extends objects by responsibilities. Remember – the
actual object to be addressed and the proxies implemented the same interface; a proxy
references another object without knowing whether it is another proxy or the desired
object. You’ll take a similar approach with the decorator. When I present you with an
example in a moment, you may not find the difference between a proxy and a decorator
clear at all – but I will discuss that below.
Take a look at what a car dealer has to offer. There are a few basic models with what feels
like a thousand trim levels. Model A is the rolling shopping bag, Model B the down-to-
earth mid-size car and Model C the upmarket variant. In addition, each model comes with
an optional navigation system, air conditioning, leather upholstery, etc. Let’s consider how
you might code this situation.
One could first come up with the idea of providing an attribute for each feature. If the
attribute is zero, it is not desired; otherwise, it has been ordered for it.
// … abridged
// … abridged
}
The disadvantage is obvious: Each object drags around a lot of unnecessary data that
has nothing to do with its actual core business. There is too much duplicate code, which
makes the project error-prone and not very maintenance-friendly. When changes are due,
existing code has to be drilled up. The OpenClosed Principle has obviously been violated.
Another approach might be to specialize, because a car with air conditioning is a car. For
example, ModelA_Climate could be defined as follows:
What would be the consequence? The number of subclasses would explode because
you would need to cover every conceivable combination. For example, you would need the
following classes: ModelA, ModelA_With_AC, ModelA_With_GPS, ModelA_
With_AC_And_GPS, ModelB, ModelB_With_AC, etc. With three basic models
with three optional extras, that’s 3 * 2 ^ 3 = 24 subclasses. Earlier, we talked about prefer-
ring composition to inheritance – and again, it shows that inheritance is not the means to
an end. In this admittedly contrived example, the weakness of inheritance is obvious.
However, I would like to assume that in practice there are cases where inheritance less
evidently leads to a rigid inflexible system.
24.1 Build Cars 305
Do you know the Matryoshka principle? You have a beautifully painted wooden doll that
encloses another wooden doll, which in turn encloses another wooden doll, which itself
encloses a wooden doll, and so on. That’s kind of how the Decorator works. Each new
feature encloses (“wraps”) the previously assembled product. Sounds complicated? It
isn’t at all.
But please note that in contrast to the composite pattern from Chap. 12, we do not
assemble branched structures here (this would then degenerate into a similar number of
variants as in the approach with inheritance), but rather close the components “around”
each other. This approach gives us the flexibility to add optional extras to the basic model
in any order and frequency.
In the package basicmodels you will find different models, for example ModelC.
@Override
public String getDescription() {
return “A car of the upper middle class“;
}
}
All special features define the methods of the interface Component. They access the
data of the referenced component and add or concatenate their own value.
@Override
public int getPrice() {
return basicComponent.getPrice() + 500;
}
@Override
public String getDescription() {
return basisComponent.getDescription() +
“ and air conditioning“;
}
}
The following paragraph shows you how to put the individual components together.
Customer requirement:
A car of the upper middle class and a satellite navigation and
air conditioning
Price:
51380
You can plug together the basic models and the equipment variants as you wish and,
above all, flexibly. Even the number of components no longer plays a role. Theoretically,
you could fit two air conditioners and three navigation units – if that’s what the customer
wants. The code is robust and maintainable because each class has a single function. High
cohesion is an indication of proper design. If the price of the navigation system goes up,
you only have to change the code in one place. And finally, let me introduce Decorator
terminology: The interface component is a component. Each base model is a concrete
component. The interface of the optional extras is a decorator; the optional extras them-
selves are the concrete decorators.
The realization of the pattern is similar to the realization of the proxy pattern. With
the Proxy I offered you the example of the UnmodifiableList. In the internet and in
the literature, you can find this example partly also with the Decorator. Is there some-
thing wrong? Where does this class belong? To come to an answer, I have to think
about the task of the UnmodifiableList. I also need to keep in mind the goal of the
Patterns. The UnmodifiableList controls access to a list. When I sort it in at the Proxy,
I emphasize the controlling nature; a Proxy controls access to the actual system, per-
haps even restricts it. The Decorator, on the other hand, adds functionality to a system
without changing it itself. Therefore, the UnmodifiableList is clearly a proxy to me.
In the next paragraph you will learn where you have already encountered the Decorator
in practice.
Use the Decorator Pattern to add scroll bars to Swing components and send data via
streams.
308 24 Decorator
Fig. 24.1 Class hierarchy of the Swing components around JComponent (simplified)
You can add scroll bars to any component: a JTree, a JPanel, a JTable, and so on. In doing
so, you plug the component together like the matryoshka and add it to a container.
A table with scrollbars is displayed on the JFrame. You can plug the components into
each other because they are all of type JComponent and the constructor of JScrollPane
expects an object of supertype Component. The interaction can be found in Fig. 24.1.
When you start looking at streams, you see the drawback of the Decorator Pattern. You
have a multitude of classes that, at first glance, all do the same thing. Looking at the
java.io package doesn’t really make any programmer happy. With knowledge of the
Decorator Pattern, you can bring order to the hodgepodge of classes.
24.2 Practical Examples 309
A FilterInputStream contains some other input stream, which it uses as its basic source of
data, possibly transforming the data along the way or providing additional functionality. The
class FilterInputStream itself simply overrides all methods of InputStream with versions that
pass all requests to the contained input stream. Subclasses of FilterInputStream may further
override some of these methods and may also provide additional methods and fields.
If you read through the quote, you will find exactly the purpose description of the Decorator
Pattern.
The idea is to extend the functionality of objects without creating subclasses. For exam-
ple, a subclass of FilterInputStream is BufferedInputStream, which writes the data to a
buffer, which improves performance. The API documentation describes the task like this:
InputStream in =
new ZipInputStream(
new BufferedInputStream(
new FileInputStream( /* filename */ )));
The inheritance hierarchy after the OutputStream class is set up accordingly. For
example, you have a FileOutputStream there that connects to a file that you write data to.
The Decorator interface is represented by the FilterOutputStream class. And from
this class, BufferedOutputStream is derived:
By setting up such an output stream, an application can write bytes to the underlying output
stream without necessarily causing a call to the underlying system for each byte written.
310 24 Decorator
With what you have read in this paragraph you can read and write a file byte by byte. The
code of such a small copy program can be found in the sample project Streams_1.
So far all is well, you can verify the Decorator pattern in the API.
You could also go one step further and wrap the FileOutputStream in a DataOutputStream
to be able to output primitive data types. In this way, you extend the functionality of the
FileOutputStream – in the sense of the Decorator Pattern – without changing it. You can
find a corresponding example in the sample project Streams_2.
} catch (IOException e) {
e.printStackTrace();
}
}
Then when a method calls write() and read(), it prints to the console:
The employee with the personnel number 4711 earns 2466.77 Euro
He's the boss
Why is this construct not a decorator? The decorator pattern allows another object to
extend the interface. However, the client code must not be affected by this; decoration
must be transparent to the client. In this example, however, that would be exactly the
case – the specialized methods cannot be called on an object of type InputStream, or
OutputStream. If the programmer wants to make use of this, he must program against
the interface of the DataInput/OutputStream.
Streams are mentioned in the Decorator Pattern context in almost every Patterns
book – including the GoF. That’s true as long as you don’t need to change the inter-
face. I have repeatedly read (quoting mutatis mutandis): “Everything that is IO is
also Decorator.” I don’t think such an absolutely worded statement is tenable.
312 24 Decorator
Fig. 24.2 UML diagram of the Decorator Pattern (example project AutoCatalog)
This time you can see the UML diagram in Fig. 24.2 from the first example project
AutoCatalog.
24.4 Summary
The Gang of Four describes the purpose of the “Decorator” pattern as follows:
The Bridge Pattern literally bridges the gap between the implementation of a functionality
and its application. It separates the abstraction from the implementation. This allows both
sides to be developed independently of each other. At the same time, the implementation
remains hidden from the client. Various examples of this can also be found in the Java
class library.
I want to define two terms before I get to the Bridge Pattern. If you are reading this section,
you may find my execution trivial. However, a precise definition of the terms abstraction
and implementation is important for understanding the Bridge Pattern.
The first concept I want to address is abstraction. When you develop a class, you always
have to decide what part of the real world you want to represent and with what accuracy.
If you’re writing software that a college uses to manage its students, you’re going to pick
out very specific relevant characteristics of a student and include those in your classes. For
example, date of birth, address, and exams passed are relevant to the university. Irrelevant
are likely to be hair color, weight, number of siblings, and many other characteristics. In
your university software, you reduce the student to the relevant characteristics; you create
a model, you abstract. For example, another abstraction is a calculator, which will be
relevant to the sample project in a moment. When you look at the calculator on your desk,
it has certain features: a specific weight, a color, different arithmetic operations, and so on.
When you develop the class Calculator, you ignore numerous features and capabilities;
you reduce the real object to the features and capabilities that are relevant to the given
context.
The next listing from the sample project Calculator shows such an abstraction. I only
print the interface here, because it is important for the client. I leave out the implementa-
tion behind it to your own analysis.
When a client creates an object of this class, it calls the methods that the interface pro-
vides. The abstraction specifies what the object can do.
If the client relies on the abstraction, it does not have to worry about the implementation
behind it. The details of the implementation may even remain hidden from him. I’ve never
been interested in how the candy machine in the office turns a euro piece into a candy bar.
Nor do I care how the calculator adds two numbers. For me as a user, the implementation,
the how of a method, is not important.
Let’s look at the implementation in the following step. In the first approach, you could
simply implement the school methods.
25.1 Two Definitions 317
You now want to change the implementation. There are alternative multiplication meth-
ods that – especially for long numbers – are much more performant than the multiplication
algorithm taught in school. If you want to change the implementation, you can simply
create a subclass that overrides the multiply() method. If you want to multiply two num-
bers using the Fast Fourier Transform (FFT), use the following class.
A client can now choose whether to use the simple multiplication algorithm or the
FFT. Accordingly, it can start an instance of the calculator either with.
or with.
By the way, I find the different multiplication methods quite exciting. If you want to
deal with FFT multiplication, I recommend the following site: http://www.inf.fh-flensburg.
de/lang/algorithmen/fft/fft.htm. You can also find the article by Prof. Dr. Lang deposited
there as “Transformations.pdf” in the directory of sample projects for this chapter.
There are many other multiplication algorithms, such as the peasant multiplication
(PM). A few of these algorithms are highly performant, others impress with elegance.
Right after you have published your calculator, extension requests come in. The calculator
should also support quadrature. Since you want to distribute this feature separately, you
develop the abstraction, the interface, further. Since squaring means multiplying a number
by itself, you can use the existing methods.
However, this solution leaves out the peasant multiplication and the school multiplica-
tion implemented by default. You cannot avoid extending the inheritance hierarchy accord-
ing to these two algorithms accordingly (Fig. 25.2).
It becomes clear that the inheritance hierarchy is too broad and too inflexible. If either
the abstraction is extended or an algorithm is added to the implementation, the number of
classes grows rapidly. Assume that division is to be replaced. The algorithm you know
from school asks how many times the divisor fits in the dividend. An alternative approach
might be to multiply by the reciprocal of the divisor. You approach the reciprocal of a
number iteratively using a formula from Newton. If you were to try to implement the divi-
sion algorithm by inheritance, you would have to consider that you have two abstrac-
tions – the Calculator and the CalculatorDeluxe. Both abstractions would want to optionally
multiply using FFT, the PM, or the school method, and optionally divide using the school
method or Newton’s method.
Conclusion: The problem is that abstraction and implementation depend too much on
each other and influence each other. The Bridge Pattern will solve this problem.
The Bridge Pattern separates the abstraction from the implementation. How does this
work? You may remember the State Pattern and the Strategy Pattern! There you defined
the behavior of an object in its own classes. You have specified an interface in your abstrac-
tion that the implementation of the classes that define the behavior must match.
The Bridge Pattern takes a similar approach. You define the abstraction with all the meth-
ods it should offer. The execution is delegated to an object of type Implementor. It is
allowed that the methods of the abstraction and the implementor have different identifiers.
Have a look at the sample project CalculatorBridge.
320 25 Bridge
The implementor can be either an interface or an (abstract) class. In the current project,
I equipped the implementor with the computational methods I learned in school.
The client creates an instance of the implementation and parameterizes the abstraction
with it:
The class diagram looks as follows at this stage of development (Fig. 25.3).
The implementor and the abstraction are connected via an aggregation – thus bridging
the gap between the two.
Up to this point, it all looks harmless. But let us convince you that with this solution you
have an incredibly powerful tool in your hands.
Further abstractions would be conceivable. If you want to calculate the square root of a
number, you can fall back on the implementation already defined. The square root of y is
the multiplication of y by the reciprocal of the square root of y. To calculate the reciprocal
of the square root of a number, there is an iterative procedure that goes back to Newton.
Starting from an approximated value, the respective consequent value is calculated – the
consequent value is calculated by addition, multiplication, difference and division alone.
The client can now choose the abstraction it wants to have and parameterize it with the
implementor. It is important that only the abstraction changes or is extended.
Regardless of the abstraction, the inheritance hierarchy can be extended according to the
Implementor class. The Implementor itself provides only the simple implementation of the
basic arithmetic operations, which are then used in the calculator. As in the previous proj-
ect, I defined the classes FFT and RBM, which override the multiplication algorithm – i.e.
do not add any fundamentally new functionality. I did not implement the respective meth-
ods. But you are welcome to try that yourself.
The client can now choose its abstraction and a suitable implementation at will.
calculator.setImplementor(new FFT());
System.out.println(“30 * 12 = “ + calculator.multiply(30, 12));
Peasant multiplication
30 * 12 = 360.0
FFT multiplication
30 * 12 = 360.0
It is important – and the class diagram in Fig. 25.4 should make this clear once again –
that the implementation can now evolve independently of the abstraction.
However, some tasks can no longer be accomplished with the basic arithmetic already
defined. For example, if the client needs a random number generator, you need to extend
both the abstraction interface and the implementor.
In this section, I will conclude by showing where you can find the Bridge in the class
library. I will also distinguish the Bridge from other patterns.
When you deal with AWT and peer classes, you dig deep into the primordial slime of
Java’s evolution. An AWT component is not drawn by Java itself, but by the operating
system. For example, the developer has a Button class that inherits from Component.
Component is the top of the inheritance hierarchy of abstraction. On the other side of the
bridge is the interface ComponentPeer, from which the implementation, for example,
the class ButtonPeer, is derived. As a programmer, you have no access to this imple-
mentation (at least in theory).
You can also find the Bridge Pattern in a completely different context – database pro-
gramming. Take a look at how you work with JDBC. You load a database driver with
Class.forName(<driver name>). Then you let it give you the connection to the
database:
Connection connection =
DriverManager.getConnection(<url>, <user>, <password>)
Fig. 25.4 UML diagram of the Bridge Pattern (sample project CalculatorBridge)
You iterate over the returned ResultSet to get the data you want. If you initialize the
abstraction – your application – with a given driver, you can choose to access an Access
database or an Oracle database or any other database. You always work with the given
25.4 Bridge – The UML Diagram 325
Connection, Statement, and ResultSet components. The question of how your query is
processed is not relevant to you, i.e.: you have no access to the implementation running in
the background.
Abstraction and implementation evolve independently of each other. Both can evolve
and be reused independently.
Facade, Adapter, Proxy, Decorator and Bridge are very similar. They use composition to
wrap another object. A method call is delegated to the wrapped object.
The task of the adapter is to relate two interfaces of different types in such a way that
they can work together. Since multiple objects can be adapted, this compensates for the
lack of multiple inheritance in Java. Adapters are typically very lean because their only
task is to relate two systems; own intelligence might provide, for example, that data is
converted.
Decorators bring intelligence of their own – they are of the same type as the wrapped
object and extend its behavior. A typical textbook example is the FileInputStream, which
is wrapped into a BufferedInputStream. Both classes are of type InputStream.
The proxy is primarily a proxy for another object. The implementation of a proxy can
be very similar to the decorator. The goal of a proxy can be to control access to the repre-
sented object.
The facade is most likely to be confused with the adapter. Its task is to simplify
access to a (sub)system. Think of booking a holiday trip, which consists of many indi-
vidual steps.
You can vary an implementation with many patterns: The Strategy Pattern lets you vary
an algorithm. The State Pattern defines different behavior depending on the context. With
the Adapter pattern, you access a different library. The Bridge Pattern differs from these
patterns in that, in addition to the implementation, the abstraction can also be developed
further. While you can implement an adapter or a facade at any time, the decision for a
bridge must be made as early as possible.
After all, the task of the bridge is to separate an implementation – i.e. the arithmetic
operations – from the abstraction – the calculator on which the user types in his calcula-
tion. The goal is to be able to develop behavior and abstraction independently of each other.
The UML diagram from the sample project CalculatorBridge can be found in Fig. 25.4.
326 25 Bridge
25.5 Summary
The Gang of Four describes the purpose of the pattern “Bridge” as follows:
Decouple an abstraction from its implementation so that the two can be varied
independently.
Combine Patterns
26
In the last 23 chapters I have explained the individual patterns. Now we will deal with an
example for which we want to use several patterns at the same time. Using a concrete
example, we will combine five different design patterns in one application. Among other
things, this is done by combining several interfaces or composing classes. What to pay
attention to will be the topic of this chapter. And I will also give you a few approaches for
your own extensions and improvements.
First, a warning: I pointed out in the introductory chapter that I think it is critical for pro-
grammers to make excessive use of patterns, see Sect. 1.1.2.4. This example is not meant
to motivate you to implement patterns “at any cost”. It is merely meant to show how pat-
terns can be combined, and what you should keep in mind when doing so.
We want to combine several design patterns using the example of an alarm system of a
rudimentary “smart home”. The basic requirements for our system are:
There should be both smoke detectors and motion sensors, but they can come from dif-
ferent manufacturers. For the test, all sensors are to be connected to a “control center”
from which they can also be triggered. When triggered, the sensors will send SMS mes-
sages with possibly different contents. In the case of smoke detection, a sprinkler system
should also be activated, and in the case of motion detection, a rotating warning light
should be switched on. Both the sprinkler system and the warning light should be able to
be switched off again by remote control. And of course, all events are logged at a central
location.
In the following subchapters, we will take a look at these things in detail. You can find
the corresponding example project under the name SmartHome.
I found the inspiration for this example on the Internet (https://ruysal.com/
post/2019-01-23-Combining-Multiple-Design-Patterns-in-Java/) and kindly got permis-
sion from the author Ramazan Uysal to use his idea. I have revised the code and adapted
it for this book.
Let me tell you in advance that you will find a class diagram under Fig. 26.1, which you
can use as a guide when working through this chapter.
The “simplest” pattern for our example is the singleton we use to provide a centralized
protocol service.
Each using object first fetches the instance and then places its message there. The log
output could then be as complicated as desired, but here we only output supplemented text
to the console.
private LogOutput() {
System.out.println(“Protocol service ready“);
}
You can see that there is already an output in the (private) constructor that the pro-
tocol service is now available. The method getInstance provides this service cen-
trally and the service itself consists only of the method showMessage, which outputs
a framed text.
26.2 Singleton: Logging 329
Two different types of sensors that can come from different producers. This sounds suspi-
ciously like an abstract factory, which we already discussed in Chap. 15. And the approach
fits pretty well here, too: First, we define the interface of a sensor factory with the methods
to build a motion sensor or a smoke detector.
We also need interfaces for the sensors. I print the version for the motion sensor here,
but it looks very similar for the smoke detector.
A class for a sensor then looks – here using the example of the motion sensor from
manufacturer A – something like this:
@Override
public String getDescription() {
return DESCRIPTION;
}
@Override
public void setLog(LogOutput l) {
this.logFile = l;
}
@Override
public void setWarningLight(WarningLight w) {
this.warningLight = w;
26.3 Abstract Factory: Building the Sensors 331
@Override
public void detected() {
// … abridged
}
}
For the motion sensor of manufacturer B and the smoke detectors of both manufactur-
ers you will find very similar codes. This should not hold any surprises. The methods
setLog and setWarningLight enable the “installation” of a sensor in a concrete
system. Since this information is not available at the time of manufacture, it must be acces-
sible via subsequent setter methods and, of course, called by the customer. In addition to
the MotionSensor interface already shown, we also implement another
SensorListener interface here. We’ll get to its details in the next section on trigger-
ing, but ultimately this is where the integration of the abstract factory with an Observer
pattern takes place, which we can use to trigger the sensors in this test case.
For now, let’s continue with the abstract factory. The only thing missing now is the
comparatively simple code for a manufacturer. Let’s look at manufacturer B this time:
@Override
public smoke detector baueRauchsensor() {
return new SmokeDetectorB();
}
}
I will spare myself a class diagram at this point. If you look at the package smart-
home.AbstractFactoy in the example project, you will find three interfaces and six
classes. The diagram looks practically the same as in Sect. 15.4, you just have to change
the names.
With this, we have implemented our first design pattern and created the approach to
connect to a second pattern, which we will look at in the next section.
332 26 Combine Patterns
In our example, we want to be able to trigger all connected sensors with a single com-
mand. Of course, this is only useful for testing purposes. In real life, each sensor would of
course react to a corresponding event within its range. Here, however, we use the Observer
pattern from Chap. 5 for the test. We thus register each installed sensor with a central
instance, which in turn sends a uniform message to each sensor, which the latter must of
course be able to process.
So first we need the rather simple interface of the SensorListener:
As you can see, only the trigger method to be addressed externally is specified here.
However, the client, which we will see later, when a sensor is installed in the house,
must also register it with the central location, the sensor system:
In the ArrayList, all sensors are recorded by means of the register method and then
addressed in the trigger method. There I use the so-called method reference this time, to
call for each sensor in the list its detected method. This way of writing is possible since
Java 8, and together with the forEach method on the ArrayList its use results in a very short
but well understandable form.
So if you look at the class model from the Observer chapter, you will now find the
interface (instead of ApartmentObserver now SensorListener) and the “Exchange” (instead
of ApartmentBoerse now SensorSystem). The equivalent of the Workers class are the sen-
sors, which are registered through the SensorListener interface common to all. They are
the recipients of a “broadcast” from the client. We don’t need an equivalent to the class
Apartment in this case, because we don’t exchange additional information about concrete
objects in another list.
26.5 Adapter: Send Notifications 333
By the way, you will notice that I have not implemented a “deregister” of a sensor. So
once a sensor is installed and registered, it would not be possible to remove it. But this
should not be a problem for you if you want to extend this example now.
We will deal with the method detected in detail a little later, because there are still
a few building blocks missing to understand it, which we have to look at first.
There is still the question of what a sensor actually does when it is triggered. According to
the requirements, it should send messages. But if you have different manufacturers and
different sensors in the game, you have to agree on some format for messages. But there is
another way.
Let’s assume that the manufacturers had agreed on a “minimum” format for SMS mes-
sages from motion sensors and one from smoke detectors. First of all, there would be those
corresponding classes. Here is the SMS of a smoke detector. That of the motion sensor is
similarly simple.
Now, when triggered, a sensor could directly create a corresponding object and call its
sendMessage method. However, the clients would then be completely dependent on the
performance of this method and would have no possibility to introduce independent
additions.
The manufacturers circumvent this with an adapter – which we already know from
Chap. 22 – whose code they make accessible to the customer. At this point, the customer
can make any adjustments (e.g. also call the original sendMessage method). First of all,
everything is based on a simple SMS interface:
Now the adapters have to implement this. For the smoke detector, let’s take a closer look:
@Override
public void sendMessage(String s) {
fromSensor.sendMessage(s);
var log = LogOutput.getInstance();
log.showMessage(“SMS dispatch of the smoke detector adapter“);
}
}
You can see here that the method sendMessage first calls the “supplied” method.
Before the call, any adjustments to the text or other measures that may be technically nec-
essary for the SMS dispatch would now be possible. Also other notification systems could
be connected here, which can pass the message text from the sensor to other recipients.
Afterwards, a log entry is generated to indicate that something has happened via the
adapter. For the motion sensor, the adapter looks basically the same, but I created a sepa-
rate version (instead of a single “message adapter”) for greater flexibility. So the response
to a motion sensor message can be different than a smoke detector trigger.
So we use the adapter pattern here a little bit differently than explained in the chapter
about the adapter. Here the manufacturer provides both the interface Sms and a basic ver-
sion of the adapter, but thereby allows the user of the sensor to “intervene” in the notifica-
tion function without intervention in the code of the sensor.
I will show you the exact integration of this adapter from a sensor when we have all the
components together. One is still missing.
A sensor that can pass on messages is already half the battle in an alarm system, but now
devices are still missing to react directly to the respective alarm. For smoke detectors, a
sprinkler system is a good choice to extinguish any burgeoning flames. For motion detec-
tors, we use warning lights in our example, which should scare away the “intruder” or alert
“guards”.
In our example, these devices must be switched on once by the respective sensors, but
must also be able to be switched off again by a remote control. For this we use the com-
mand pattern from Chap. 9.
Again, the two variants for smoke detectors and motion sensors are almost identical. So
I print here one variant, the other you will find of course also in the sample code.
So let’s take a look at the warning light as an example:
26.6 Command: Actuators and Remote Control 335
And an encapsulated command for the warning light then has this appearance:
@Override
public void execute() {
var logFile = LogOutput.getInstance();
warninglight.off();
logFile.showMessage(“Warning light off“);
}
}
This is a very simple version, but of course you can extend it as you wish. In the version
presented, a command can be associated with the button at some time. If the button is
pressed, the command is executed.
Now we have all the pieces together to look at the method detected of a sensor in its
entirety. Here is an example:
@Override
public void detected() {
var s = “”“
Motion sensor - Manufacturer A
Movement detected
“”“;
if (logFile == null)
s =
s.concat(“No protocol service configured!\n“);
else
logFile.showMessage(“MOVEMENT!“);
if (warningLight == null)
s = s.concat(“No alarm annunciator configured!\n“);
else {
var turnOn =
new WarningLightSwitchOn(warningLight);
turnOn.execute();
}
Sms sms =
new MotionSensorSmsAdapter(new MotionSms());
System.out.println(“Motion Sensor - Manufacturer A“);
sms.sendMessage(s);
}
}
26.9 The Client 337
I have designed the method of the motion sensor of manufacturer A bit more extensive
than the others. Here we build up a text which, depending on the configuration of a proto-
col service and a warning light, contains appropriate information or carries out the respec-
tive actions. Finally, the message with the final text is sent via the adapter. Here we connect
the products of the abstract factory with the adapter and also with the command pattern.
Please note the multi-line string at the beginning of the method. This possibility of text
representation called Text Block has become available in production with Java 15 (JEP
378). It is also already available as a preview in Java 13 (JEP 355) and Java 14 (JEP 369).
In essence, we can now use multi-line text starting and ending with three quotes even
without control characters for the line break in the source code. However, we are still deal-
ing with the String data type. The most important thing is the indentation depth of the
starting quotation marks, because whitespaces to the left of them (spaces, tabs) do not
become part of the captured text. More precisely, all whitespaces in all lines of a text block
that are to the left of the leftmost character of any line are not included.
In the example above, this means that the line “Motion sensor – Manufacturer A” is
left-justified, although it is indented by 16 characters in the source code. However, the line
below “Motion detected” is then output indented by four characters.
Handling whitespaces in text blocks also takes up a large portion in the above JEPs.
Please be sure to check them out if you want to work with text blocks.
NetBeans marks the characters that actually belong to the string with a colored back-
ground, so that you can easily see which spaces or tabs belong and which do not. If you try
moving the line with “Motion sensor …” one or two characters to the left by deleting
preceding spaces, you will see from the changed background marking what this will do to
the other lines.
The detected methods of the other sensors do not use text blocks in this example
and are also simpler in design. Therefore I do not show them here.
Now we have a total of five patterns “plugged together”. Let’s have a look at the class
diagram, which you can find in Fig. 26.1.
If you compare this diagram with the UML diagrams of the individual patterns from the
previous chapters, you should be able to recognize the individual parts, but also identify
the new connections.
All that’s missing now is the client, which assembles the sensors from the various manu-
facturers into one system, triggers all of them on a test basis, and then also switches off the
sprinkler system and the warning light.
338 26 Combine Patterns
The main method and the constructor of the SmartHome class are simple at first:
SmartHome() {
init();
test();
}
The init method now connects four products of the abstract factory – one motion
sensor and one smoke detector from each manufacturer – with the singleton for the log
output, the warning light or the sprinkler system, which will later receive the commands to
switch on, and the control center, where the sensors act as observers. Finally, a remote
control is created, which can take care of switching off the systems.
void init() {
log = LogOutput.getInstance();
SensorFactory factory;
smokeDetetector1.setLog(log);
smokeDetector2.setLog(log);
motionSensor1.setLog(log);
motionSensor2.setLog(log);
26.9 The Client 339
smokeDetector1.setSprinkler(sprinkler);
smokeDetector2.setSprinkler(sprinkler);
motionSensor1.setWarningLight(warningLight);
motionSensor2.setWarningLight(warningLight);
// … abridged
}
In the end, these are all comprehensible steps that are actually involved in assembling
an alarm system.
And then in the test method we try out everything. First, all sensors get the command
to trigger their alarm. Then, via the remote control, first the warning light and then the
sprinkler system are switched off again.
void test() {
System.out.println(“Starting tests:“);
sensorSystem.trigger();
remote.setCommand(
new warningLightSwitchOff(warningLight));
remote.pressButton();
remote.setCommand(
new SprinklerSwitchOff(sprinkler));
remote.pressButton();
}
// … abridged
}
340 26 Combine Patterns
When you run the program, you should get the following output:
The first line comes from the singleton and is output during its initialization. After that,
the test method reports and then all sensors one after the other, first the smoke detectors,
then the motion sensors. For the motion sensor of manufacturer A you can also see two
error messages in the sample output. For this run, I commented out the LogOutput and
WarningLight assignments to motion sensor 1 in the init method to demonstrate its fault
tolerance. If you try this with the other sensors, NullPointerExceptions are thrown in this
version.
26.11 Summary 341
26.10 Considerations
The example is intended to show you a way to combine different patterns in a meaningful
way. There are two fairly obvious approaches to extending and modifying this example:
First, the detect methods should all be extended to also respond to missing log outputs
or actuators. Alternatively, you can make the detect methods very rudimentary and even
move the log entry and triggering of the warning light or sprinkler system to the respective
adapter. Feel free to experiment with this a bit.
Or you can go the other way, and remove one of the patterns (according to my introduc-
tory warning in this chapter). Do you really need the adapter, or can’t you do without it?
What alternatives would you have then? Not everything that is technically possible neces-
sarily makes sense at this point. For your respective tasks, always think carefully about
what is necessary, what makes sense, and what might even be annoying.
You should be able to find the connections between the patterns in the source code:
• The singleton is used in various places by requesting the instance and then calling its
showMessage method.
• In the sensors that are created in the abstract factory, the interface SensorListener
is also implemented, which identifies each sensor as an observer.
• Also in the sensors you will find the call of the respective adapter in the detected
methods.
• And in the detected methods as well as in the remote control there’s also the use of
the Command Pattern.
• The init method of the client contains the registration of the observers as well as
the creation of the actuators warning light and sprinkler, which act as command
receivers.
• In the client’s test method, the Observer pattern and the Command pattern are then
actively used.
26.11 Summary
• The composition of pattern components in classes of other patterns also enables the
connection of multiple patterns.
• The more patterns are connected in a program, the more complex the structure
can become.
• Caution is required in order not to lose the overview and the respective functionalities
offered. The structuring in packages can support this.
Concluding Remarks
I hope you enjoyed reading the book, and that you may have learned something along the
way. As mentioned at the beginning, you should use it as a reference book if you don’t
quite remind any individual issues. Now would also be a good time to take another look at
the chapter on design principles (Chap. 2) and then discover them again in the patterns
mentioned.
If you have discovered any inconsistencies or errors from your point of view, I would
be very grateful for your feedback. You can best reach me at designpatternsjava16@
gmail.com.
You are free to use and manipulate the source code of this book for your private pur-
poses – that’s what I’m providing it for.
Thank you so much for sticking it out this far.
Olaf Musch