Object Oriented Development
Object Oriented Development
Development
A Hands-On Approach
Leif Lindbäck
i
Revision History
ii
License
Except for figures 5.7, 5.8, 5.11, 5.14, 7.1, 7.2, 7.6, 8.1, 9.4, 9.11, 9.13, and 9.18 A First Course
in Object Oriented Development, A Hands-On Approach by Leif Lindbäck is licensed under a
Creative Commons Attribution 4.0 International License, see http://creativecommons.
org/licenses/by/4.0/.
iii
Preface
This text is a compilation of material developed during fifteen years of teaching a first course
on object oriented development. Students taking that course have previously taken one 7.5
hp credit course in object-oriented programming. Thus, the reader is assumed to have basic
knowledge of Java programming. Important concepts, in particular objects and references, are
repeated in chapter 1.
Things that are crucial to remember, but easy to miss, are marked with an exclamation
mark, like this paragraph. Forgetting the information in such paragraphs might lead to severe !
misunderstandings.
Paragraphs warning for typical mistakes are marked NO!, like this paragraph. Such para-
graphs warn about mistakes students have frequently made.
NO!
There are Java implementations of all UML design diagrams in Appendix C. The purpose
is to make clearer what the diagrams actually mean. The analysis diagrams can not be imple-
mented in code, since they do not represent programs. There is a NetBeans project with all
Java code in this book, that can be downloaded from GitHub [Code].
iv
Contents
Revision History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv
I Background 1
1 Java Essentials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1 Further Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Arrays and Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.6 Javadoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.7 Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.8 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.9 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.1 Why Bother About Object Oriented Design? . . . . . . . . . . . . . . . . . . 13
2.2 Software Development Methodologies . . . . . . . . . . . . . . . . . . . . . 13
2.3 Activities During an Iteration . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4 Unified Modeling Language, UML . . . . . . . . . . . . . . . . . . . . . . . 15
II Course Content 19
4 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.1 UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.2 Domain Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.3 System Sequence Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
v
Contents
5 Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.1 UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.2 Design Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.3 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5.4 A Design Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.5 Designing the RentCar Case Study . . . . . . . . . . . . . . . . . . . . . . 58
5.6 Common Mistakes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6 Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.1 Dividing the Code in Packages . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.2 Code Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.3 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6.4 Code Smell and Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.5 Coding Case Study . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
6.6 Common Mistakes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
7 Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
7.1 Unit Tests and The JUnit Framework . . . . . . . . . . . . . . . . . . . . . . 121
7.2 Unit Testing Best Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
7.3 When Testing is Difficult . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
7.4 Unit Testing Case Study . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
7.5 Common Mistakes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
Bibliography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
vi
Contents
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
vii
Part I
Background
1
Chapter 1
Java Essentials
This text assumes previous knowledge of an object oriented programming language. All code
listings are written in Java. This chapter repeats important concepts of Java, but does not cover
the whole language.
1.2 Objects
The goal of object-oriented programming is to declare classes, which are groups of data and
methods operating on that data. A class represents an abstraction, for example person. An
object represents a specific instance of that abstraction, for example the person you. Whenever
a new person shall be represented in the program, a new object of the Person class is created.
This is done with the operator new, as illustrated on lines one and five in listing 1.1.
2
Chapter 1 Java Essentials
Two different objects, representing the persons Alice and Bob, are created in listing 1.1.
Note that when Alice moves to another address, line eight, Bobs address remains unchanged,
since alice and bob are different objects. The output when running the program is provided
in listing 1.2, and the source code for the Person class is in listing 1.3.
A constructor is used to provide initial values to an object. In listing 1.3, the value passed to
the constructor is saved in the object’s field on line nine. Sending parameters to a constructor
is just like sending parameters to a method. More than one constructor is needed if it shall
be possible to provide different sets of initialization parameters. The constructor on lines four
to six is used if no home address is specified when the object is created, the constructor on
lines eight to ten is used when a home address is specified. Note that, on line five, the first
constructor calls the second constructor, using null as the value of the home address.
The variable this always refers to the current object. The variable this.homeAddress
on line nine in listing 1.3 is the field declared on line two, homeAddress on line nine is the
constructor parameter homeAddress declared on line eight. These two are different variables.
A word of warning: use static fields and methods very restrictively! Static fields are shared
3
Chapter 1 Java Essentials
by all objects of the class. If for example the person’s address was static, all persons would
have the same address. Such a program would be useless. Since fields can not be static, neither
can methods since static methods can access only static fields. Static fields and methods are
normally not used at all, except in a few, very special, cases.
1.3 References
When the new operator is used to create an object, it returns a reference to that object. A
reference can, like any other value, be stored in variables, sent to methods, sent to constructors,
etc. This is illustrated in listing 1.4, which contains a program where a person places food in a
dog’s bowl. First, a Bowl object is created on line three. The reference to that object is stored
in the bowl variable and passed to the constructor of a Person object on line four. On line
16, the Person object stores the reference in the bowl field, declared on line 12. Then, on
line six, the main method calls the feedDog method in person. In feedDog, the method
addFood is called in the previously created bowl, on line 20. This shows how an object
(bowl) can be created in one place (main), passed to another object (person) and used there.
4
Chapter 1 Java Essentials
29 }
30 }
Listing 1.4 The bowl object is created in the main method and a reference to it is
passed to the person object, where it is used.
It is better to use a java.util.List if the number of elements is not both fixed and
known, see listing 1.6.
1 import java.util.ArrayList;
2 import java.util.List;
3 ...
4 List myList = new ArrayList();
5 myList.add("Hej");
6 myList.add(3);
A List can contain objects of any class, listing 1.6 stores a String on line five and an
Integer on line six. This means that when reading from the List, the type of the read
element will always be java.lang.Object. It is up to the programmer to know the actual
type of the element and cast it to that type. This procedure is error-prone, it is better to
restrict list elements to be objects of one specific type. This is done in listing 1.7, where
adding <String> on line four specifies that the list may contain only objects of type String.
Adding <> specifies that this holds also for the created ArrayList.
1 import java.util.ArrayList;
2 import java.util.List;
3 ...
4 List<String> myList = new ArrayList<>();
5 myList.add("Hej");
6 myList.add("Hopp");
5
Chapter 1 Java Essentials
When list content is restricted to one type, it is possible to iterate the list using a for-each
loop, see lines eight to ten in listing 1.8.
1 import java.util.ArrayList;
2 import java.util.List;
3 ...
4 List<String> myList = new ArrayList<>();
5 myList.add("Hej");
6 myList.add("Hopp");
7
8 for(String value : myList) {
9 System.out.println(value);
10 }
1.5 Exceptions
Exceptions are used to report errors. When an exception is thrown, the method throwing it
is immediately interrupted. Execution is resumed in the nearest calling method with a try
block. This is illustrated in listing 1.9. On line 34, the addFood method checks if the bowl
would become overfull when more food is added. If so, instead of adding food, it throws an
exception (lines 35-40). This means line 42 is not executed. Instead, the program returns to
the calling statement, which is on line 24, in the feedDog method. However, that line is not
in a try block, which means execution returns to the statement where feedDog was called.
That call is made on line eight, which is in a try block. Execution then jumps immediately
to the corresponding catch block. This means line eleven is the line executed immediately
after throwing the exception on line 35.
6
Chapter 1 Java Essentials
12 }
13 }
14 }
Listing 1.9 An exception is thrown if food is added to the bowl when it is already full.
All methods in listing 1.9 that may throw an exception declare that, with throws Exception
in the method declaration. This is required if the thrown exception is a checked exception, but
not if it is a runtime exception. An exception is a runtime exceptions if it inherits the class
java.lang.RuntimeException.
7
Chapter 1 Java Essentials
1.6 Javadoc
Javadoc is used to generate html pages with code documentation, like the documentation of
the Java APIs at http://docs.oracle.com/javase/8/docs/api/. It is strongly rec-
ommended to write Javadoc for all declarations (classes, methods, fields, etc) that are not
private. A Javadoc comment is written between /** and */. The tags @param and @return
are used to document method parameters and return values. See listing 1.10 for examples.
1 /**
2 * A person that lives at the specified address.
3 */
4 public class Person {
5 private String homeAddress;
6
7 /**
8 * Creates a new <code>Person</code>.
9 */
10 public Person() {
11 this(null);
12 }
13
14 /**
15 * Creates a new <code>Person</code> that lives at the
16 * specified address.
17 *
18 * @param homeAddress The newly created
19 * <code>Person</code>’s home address.
20 */
21 public Person(String homeAddress) {
22 this.homeAddress = homeAddress;
23 }
24
25 /**
26 * @return The <code>Person</code>’s home address.
27 */
28 public String getHomeAddress() {
29 return this.homeAddress;
30 }
31
32 /**
33 * The the <code>Person</code> moves to the specified
34 * address.
35 *
36 * @param newAddress The <code>Person</code>’s new
37 * home address.
38 */
8
Chapter 1 Java Essentials
1.7 Annotations
Annotations are code statements that are not executed. Instead, they provide information
about a piece of source code for the compiler, JVM or something else. Annotations are usu-
ally used for properties unrelated to the functionality of the source code, for example to con-
figure security, networking or tests. An annotation starts with the at sign, @, for example
@SomeAnnotation. Annotations may take parameters, for example @SomeAnnotation(
someString = "abc"). An example is found on line 20 in listing 1.11.
1.8 Interfaces
An interface is a contract, specified in the form of method declarations. A class implementing
the interface must fulfill the contract, by providing implementations of the methods. The
method implementations in the implementing class must do what is intended in the method
declarations in the interface. This should be documented in javadoc comments. Note that
the interface contains only declarations of methods, there are no method bodies. Listing 1.11
shows an interface that defines the contract Print the specified message to the log, lines one to
eleven. It also shows a class that implements the interface and fulfills the contract, lines 12-24.
1 /**
2 * An object that can print to a log.
3 */
4 public interface Logger {
5 /**
6 * The specified message is printed to the log.
7 * @param message The message that will be logged.
8 */
9 void log(String message);
10 }
11
12 /**
13 * Prints log messages to <code>System.out</code>.
14 */
15 public class ConsoleLogger implements Logger {
16 /**
17 * Prints the specified string to <code>System.out</code>.
9
Chapter 1 Java Essentials
The @Override annotation on line 20 in listing 1.11 specifies that the annotated method
should be inherited from a superclass or interface. Compilation will fail if the method is
not inherited. Always use @Override for inherited methods since it eliminates the risk of
accidentally specifying a new method, for example accidentally naming the method logg
instead of log in the implementing class in listing 1.11.
1.9 Inheritance
When a class inherits another class, everything in the inherited class that is not private becomes
a part also of the inheriting class. The inherited class is often called superclass and the inherit-
ing class is called subclass. This is illustrated in listing 1.12, where methodInSuperclass
is declared in Superclass on line two, but called on line eleven as if it was a member of
Subclass. Actually, it has become a member also of Subclass, because it has been in-
herited.
A method in the subclass with the same signature as a method in the superclass will override
(omdefiniera) the superclass’ method. This means that the overriding method will be executed
instead of the overridden. A method’s signature consists of its name and parameter list. In
10
Chapter 1 Java Essentials
listing 1.13, the call to overriddenMethod on line 16 goes to the method declared on line
nine, not to the method declared on line two.
Do not confuse overriding with overloading, which is to have methods with same name but
different signatures, due to different parameter lists. This has nothing to do with inheritance.
The keyword super always holds a reference to the superclass. It can be used to call the
superclass from the subclass, as illustrated on line ten in listing 1.14.
11
Chapter 1 Java Essentials
16 }
17 }
Listing 1.14 Calling the superclass from the subclass. This program prints Printed
from Subclass, followed by Printed from Superclass
To declare a class means to define a new type, therefore, the class named Subclass of
course has the type Subclass. When inheriting, the subclass will contain all methods and
fields of the superclass. Thus, the subclass will also have the type of the superclass, the
subclass in fact becomes also the superclass. This means that an instance of the subclass can
be assigned to a variable of the superclass’ type, see line 18 in listing 1.15. When a method is
called, as on line 19, the assigned instance is executed, not the declared type. This means the
method call goes to the method declared on line ten, not to the method declared on line two.
Listing 1.15 Calling a method in an instance of the subclass, that is stored in a field of the
superclass’ type. This program prints Printed from overriddenMethod in subclass, followed by
Printed from overriddenMethod in subclass
12
Chapter 2
Introduction
Before starting with object oriented analysis and design, it is necessary to understand how
those activities fit in the software development process. This chapter gives a general under-
standing of different activities performed in a programming project, and explains when and
why to do analysis and design.
13
Chapter 2 Introduction
14
Chapter 2 Introduction
work close to users and frequently discuss the functionality. In particular, each iteration shall
start with discussing requirements. Requirements analyses is not covered further in this text.
Analysis means to create a model, a simplified view, of the reality in which the system
under development shall operate. That model will consist of classes, attributes, etc. However,
it shall not describe the program that shall be developed, but rather describe the reality in
which the program shall operate. The purpose is to gain a better understanding of this reality,
before thinking about the program. Analysis is covered in chapter 4.
Design is an activity where we reflect on the code that shall be developed and create a plan
that gives a clear understanding of which classes and methods the code will contain, and how
they will communicate. To write a program without a plan is as inadequate as building a house
without a plan. Design is introduced in chapter 5.
Coding is of course the most important part of development, it is code quality alone that
decides if the program works a intended. The other activities have no other purpose than to
improve the quality of the code. This does not mean that the other activities can be neglected, it
is impossible to develop code of high quality without carefully performing all other activities.
Guidelines for writing high-quality code are covered in chapter 6.
Testing shall, as described above, be automated and extensive. Tests that are easy to execute
and clearly tell the state of the program are extremely valuable. They facilitate development
immensely since they make developers confident that the program works, also when changing
or adding code. Testing is covered in chapter 7.
Integrate means to add newly developed code to a repository with all previously devel-
oped code, and to verify that both new and previously developed code still work as intended.
The bigger the program and the more developers involved, the harder this process is and the
more important that it is well defined how to do it. Extensive and automated tests help a lot.
Integration is not covered further in this text.
Evaluation of code that was written during an iteration, is an important last activity of the
iteration. An iteration can not be ended without demonstrating the program to the clients and
gathering their opinions. The client’s opinions are added to the requirements and are managed
in coming iterations. This is not covered further in this text.
These are the main activities performed during each iteration, a typical iteration length is
one or two weeks. However, each developer shall also have a smaller, personal iteration,
which consists of designing, coding, testing and integrating. These four activities make an
indivisible unit of work, coding shall never be done alone without the other three activities.
Design is needed to organize the code and make sure it has the two required properties being
easy to modify and being easy to understand. Testing is needed to make sure the code works
as intended. Tests are also needed to show if the code still works as intended after coming
iterations. Integration with other code is needed because code parts are of no use unless they
work together.
15
Chapter 2 Introduction
plans must contain symbols of classes, methods, etc, and to understand each other’s plans we
must agree on the symbols being used. To define those symbols is the purpose of the unified
modeling language, UML. UML is a vast standard, this text covers only the small fraction
needed to draw the plans that will be developed here.
UML defines different types of diagrams and the symbols that can be used in each of those
diagrams. Here, we will use class diagrams to give a static picture of something, and sequence
or communication diagrams to illustrate events following each other in time. When using
UML, it is important to understand that it does not say anything about the meaning of the
diagrams or symbols. For example, during analysis we use classes in a class diagram to
illustrate things in the reality. During design we use the same class symbols in class diagrams
to illustrate classes in an object oriented program. Thus, a class symbol can represent an
abstraction in the reality, a class in an object oriented program, or any other thing we choose
to let it represent. UML just defines what the symbol looks like.
16
Chapter 3
17
Chapter 3 The Case Study
18
Part II
Course Content
19
Chapter 4
Analysis
The purpose of analysis is to create a model, a simplified view, of the reality in which the
system under development shall operate. That model will consist of classes, attributes, method
calls, etc. However, it shall not describe the program being developed, but rather the reality
in which that program operates. The purpose is to gain a better understanding of this reality
before thinking about the program. This chapter shows how to develop a domain model and a
system sequence diagram. It also covers the UML needed for those two diagrams.
4.1 UML
This section introduces the UML needed for the domain models and system sequence dia-
grams drawn in this chapter. The UML diagrams used are class diagram and sequence dia-
gram. More features of these diagrams are covered in following chapters.
It can not be stressed enough that UML does not say anything about the meaning of dia-
grams or symbols. For example, a UML class in a UML class diagram is just that: A UML
class. It can represent something in the real world, like a chair, it can represent something in
!
a program, like a Java class, or it can represent something completely different.
When drawing a UML diagram, the meaning of the diagram and its symbols must be de-
fined. That is why specific diagrams have specific names, for example domain model. When
a diagram is given a well-defined name, everyone knows what it depicts and what its symbols
represent.
Class Diagram
A class diagram gives a static picture of something. It shows no flow or progress in time,
but only what classes there are, what they contain and how they are connected to each other. !
There is no notion at all of time in a class diagram.
The content of a class diagram might be a snapshot showing how things look at a particular
instant in time, or it might be the sum of everything that has existed during a specific time
interval, or it might be everything that will ever exist. This must be defined by the diagram
author.
20
Chapter 4 Analysis
21
Chapter 4 Analysis
commonly used if class-association-class shall be read from right to left, or bottom up.
UML has different arrows, with different meaning. The arrows must look exactly as in
figure 4.2.
!
Figure 4.2d tells how many instances of each class are involved in the association. In this
example, there is exactly one instance of Flight, and five to fifty instances of Passenger.
This means the passengers travel with the same flight, which can take a maximum of fifty
passengers. Also, the flight will not take place if there are less than five passengers. It is pos-
sible to use the wildcard, *, when specifying the number of instances. It means any number,
including zero.
Sequence Diagram
A sequence diagram shows how instances send messages to each other. The UML term is
message, not method call. The messages in the diagram form a sequence of events, that
happen in a specified order in time.
Figure 4.3a shows how to draw an instance. The word before the colon, myObj, is the name
of the instance and the word after the colon, MyClass, is the name of the class. Both names
are optional. The dashed line, called lifeline, is where messages to and from the instance are
anchored.
Figure 4.3b shows communication between two objects. Time flows from top to bottom, the
first message is bookSeat, which is followed by checkInLuggage. The message bookSeat
has a return value, which has the name accepted. The message checkInLuggage has a pa-
rameter, which has the name wheight. The thicker parts of the lifelines are called activation
bar, and means the instance is active during that period in time. If the sequence diagram de-
picts an object oriented program, the extent of an activation bar corresponds to execution of
a method. Figure 4.3c illustrates exactly the same as 4.3b, but without activation bars. This
format is preferred if it is not important to show when instances are active.
22
Chapter 4 Analysis
Remember that different arrows have different meaning. The arrows must look exactly as
in figure 4.3. Generally, most things in UML are optional, but if used they must look exactly !
as defined in the specification.
(a) (b)
(c)
Flow control is illustrated with combined fragments, which are the boxes drawn around
the messages in figure 4.4. A combined fragment consists of an interaction operator and an
interaction operand. The operators used here are opt, which illustrates an if statement, see
figure 4.4a; alt, which illustrates an if else statement, see figure 4.4b; and loop, which
illustrates an iteration, see figure 4.4c. The operands are the boolean expressions in square
brackets. In this example, figure 4.4a says that the passenger checks in luggage if the operand
hasLuggage is true. Figure 4.4b says that the passenger checks in luggage if hasLuggage
is true, and checks in without luggage if hasLuggage is false. Finally, figure 4.4c says that
the steward continues to serve meals while unservedPassengers is true. UML does not
specify operand syntax, any text is allowed in an operand.
To avoid confusion, it is always important to follow naming conventions. UML has mul-
tiple sets of naming conventions, the conventions used here are the same as in Java. Class
names are written in pascal case, LongDisctanceFlight; object names, attribute names, !
method names and variable names are written in camel case, economyClassPassenger,
luggageCount, checkInLuggage, unservedPassengers.
23
Chapter 4 Analysis
Notes
Both class and sequence diagrams (and all other UML dia-
grams) can have comments, see figure 4.5. A comment is an
explaining text, a note to the reader, that is not part of any of
the elements in the diagram. Comments are often called notes
in UML. A note is anchored to the element it explains with a
dashed line, as in figure 4.5.
Figure 4.5 A UML comment
24
Chapter 4 Analysis
Figure 4.7 The first draft of the domain model, after noun identification
There are many different proposals for categories. Here, the following quite short and
simple set is used,
25
Chapter 4 Analysis
The best way to create a category list is to simply consider each row in the category list
and try to imagine class candidates belonging to that category. Write down all classes that are
found, at this stage it is not interesting if the class is already listed or if it is relevant. Table 4.1
is a category list for the Rent Car case study.
Table 4.1 Category list for the Rent Car case study.
Next, all class candidates are added to the domain model, which now looks like figure 4.8.
26
Chapter 4 Analysis
Figure 4.8 The domain model, with classes from the category list added
Also remember that it is impossible to create a perfect model, there is a limit to how much
time it is meaningful to spend. Therefore, if it is really unclear if a class shall be removed or
not, just let it stay, at least for now.
Now consider figure 4.8, is there something that ought to be changed, in order to make the
DM clearer?
• The class Address is no longer needed, since there are the classes OfficeAddress
and CustomerAddress, and no more addresses need to be specified.
• The class Program was created since the requirements specification stated what the
program under development should do, but should it really be included in the DM?
The argument against is that the DM shall show only the reality, if Program is kept,
we have started to think about programming. In fact, all the other classes are a model
that shall be present inside the program. The argument for, on the other hand, is that
the program is in fact present in the reality. If a person, completely ignorant regarding
programming, where to write down all entities present in the rental office, the list would
include program (or system or something similar), since the cashier obviously interacts
with the computer.
This problem has no definite answer, it can be discussed endlessly. However, since this
text is a first course in analysis, Program is removed. The unexperienced developer
easily falls into the trap of modeling the program, instead of the reality, if the class
Program is present.
That is enough for now. If there are more irrelevant classes, they can be removed later,
before the DM is finalized.
27
Chapter 4 Analysis
• Name is a string. Unless it is relevant to split it into first name and last name, it can be
an attribute of Customer. It can also be an attribute of Cashier, if needed.
• Amount is a number, and could become an attribute of Cash and Balance. But then
what about Currency? Is that not a string that should be an attribute of Amount? This
is something that should be discussed with the customer, but now let’s just decide we
do not need to keep track of currencies. Therefore, Currency is removed and Amount
becomes attributes of Cash and Balance, and also of Change, Payment, FixedCost,
KilometerCost and InsuranceCost.
• Is CarDescription an attribute of Car? No, since it most likely contains quite a lot
of information, like model, model year, size, etc. All this can not be represented as a
single string.
28
Chapter 4 Analysis
That is enough, remember that there is no point in minimizing the number of classes. The
domain model with attributes is depicted in figure 4.9.
29
Chapter 4 Analysis
Start with the most central associations. Since it is all about renting a car, that could be for
example Customer performs Rental, Car isRentedIn Rental, Payment pays Rental
and Car isOwnedBy RentalCompany. Then continue, following the guidelines above. The
result can be seen in figure 4.10.
30
Chapter 4 Analysis
Common Mistakes
Since creating a domain model is a matter of discussion and, at least to some extent, a matter
of opinion, it might be difficult to assess the quality of the resulting DM. There are many
ways to create a good model, but also many ways to create a bad model. This section explains
some typical mistakes, resulting in a model of low quality. Such a model might not be plainly
wrong, but is of little help to the developers.
The first, and most obvious, mistake is not to model reality, but instead regard the DM
as a model of a program. This normally also means that some notion of time is assigned
to the DM. Things are thought happen in a sequential order, whereas a DM (or any UML
class diagram) says absolutely nothing about time or order of events. A class Program or
System often becomes essential in such a “programmatic DM”, but be aware that the role NO!
of the program can be assigned to any other class as well. Also, an association is considered
to be some kind of method call, instead of a relation. Finally, the actor, which is the cashier
in the case study, becomes the user of the program. Figure 4.11 shows an example of a
“programmatic domain model” where the class Office represents the program.
Another, less obvious, mistake, is to create a DM that correctly models the reality, but does
not convey any information besides what is already in the requirements specification. In such
a “naïve domain model”, the actors, customer and cashier in the case study, become central
classes with many outgoing associations. Other classes tend to be associated only with one
of the actors. This kind of DM is in fact just a visual representation of the specification. NO!
It focuses on what the actors do, modeling flow, instead of giving a static picture of what
exists. This might not be completely wrong, but adds little value to what already exists, in
text. Figure 4.12 is an example of a naïve DM, compared to the DM of figure 4.10, it does
not say much.
31
Chapter 4 Analysis
NO!
Figure 4.11 This is not a correct domain model. The modeler has tried to create a program, instead of modeling
the reality in which the program acts.
NO!
Figure 4.12 This domain model does not add any extra value, or new information.
32
Chapter 4 Analysis
NO!
Figure 4.13 This extract of the RentCar case study has a class, Rental, with unnecessarily many associa-
tions.
33
Chapter 4 Analysis
34
Chapter 4 Analysis
Figure 4.15 The requirements specification for the RentCar case study.
internally, in the system, searchInDatabase is therefore not an adequate name. This system
operation also takes parameters, namely the customer’s description of the desired car. We
could write a long list with these parameters, e.g., size, price, model, desired features (for
example air condition), etc. The downside of such a solution is that a lot of time would be spent
deciding exactly which parameters to include. Also, if the set of parameters would change,
we would have to change this system operation. And the set of parameters likely will change,
as development continues and the needs the system shall meet become clearer. Therefore,
it is better to use an object as parameter. This object, which can be called wishedCar, has
attributes that define the set of possible wishes. Note that types, whether primitive or classes,
are not of interest in the SSD. Now, exactly which the wishes are is not necessary to decide.
Also, if the set of possible wishes changes, that is an internal matter for the class of this object,
no changes are required to the system operation.
Now the system operation and its parameters are identified. Next, it must be decided if the
operation has a return value, and, if so, which return value? The answer is found in bullet four,
which tells that the system gives a positive answer to the search, and in bullet five, which tells
that the cashier describes the found car. Considering only bullet four, it might seem adequate
to use a boolean return value, but bullet five clearly states that the return value must include a
description of the found car. Therefore, also the return value can be an object, which can be
called foundCar. The system sequence diagram with this first system operation is depicted
35
Chapter 4 Analysis
in figure 4.16. Note that activation bars are omitted, which is practice in system sequence
diagrams. It is not relevant to know when actors and systems are active. Also, trying to decide
this tends to lead to long and quite meaningless discussions.
Figure 4.16 The system sequence diagram, after the first system operation has been created.
Continuing the same way, bullet six in the requirement specification does not involve any
interaction with the system, neither does bullet seven. Bullet eight defines the second sys-
tem operation, which can be called registerCustomer. An alternative name could be
registerCustomerData, but it is usually unnecessary to include the word data, since more
or less all operations include data in some way. The parameters of this operation are name,
address and driving license number. These could very well be joined in an object, customer,
according to the reasoning above, for the searchMatchingCar system operation. But since
this parameter list is defined exactly in the specification, it is less likely that it changes in the
future. The latter alternative is chosen, since that clearly shows that the parameters are known.
Bullet nine is a system operation, bookCar. It is a bit unclear if it shall take any parameters.
It could be argued that it does not take any parameters, in which case the system must keep
track of the car returned in the searchMatchingCar operation, and book that same car. It can
also be argued that the car to book shall be specified in the bookCar operation. If so, the name
of the parameter shall be the same as the name of the return value of searchMatchingCar,
to show that it is in fact the same car. The latter alternative is chosen, mostly since the for-
mer would require us to remember that the car returned from searchMatchingCar must be
stored, and is therefore a bit unclear.
Bullet ten is no system operation. It describes work done internally, inside the system, and
does not involve any interaction with an actor. Bullet eleven also is no system operation, since
it takes place only between customer and cashier. Bullet twelve is a system operation, it can
be called simply pay, and take the parameter amount. The SSD now looks as in figure 4.17.
In bullet 13, a receipt is printed as a result of the pay operation. Does this mean receipt
is a return value of pay? The answer depends on if the printer is considered to be part of the
system under development (SUT), or not. If the printer is part of the system, the printing is an
internal matter and shall not be included in the SSD. The receipt then becomes a return value.
On the other hand, if the printer is not part of the SUT, it becomes an external system, called
from the SUT. This means there is an interaction between the SUT and an external entity,
which shall be included in the receipt. The latter alternative is chosen, mainly to illustrate how
such an interaction looks in the SSD, se figure 4.18.
Continuing, bullets 14-16 are either internal or external, and to not bring any interaction
36
Chapter 4 Analysis
Figure 4.17 The system sequence diagram, with more system operations added.
Figure 4.18 The system sequence diagram, with call to external system.
between the SUT and its actors. Therefore, they do not generate any system operation. That
concludes the basic flow, next the alternative flow is considered. The alternative flow specifies
a loop, including bullets two, three and four in the basic flow. The iteration around these
bullets continues until a matching car is found. Note that the specification is incomplete, it
does not allow the customer to give up and leave without renting a car. Of course this must be
changed in coming iterations of the development. The loop can be modeled as in figure 4.19.
There are a two things worth highlighting regarding the loop. First, the guard noMatchingCar
is a free text boolean condition, it does not correspond to a boolean expression in the program.
The condition can become true because of an action taken by the system, the actor or some-
thing completely independent of both system and actor. Second, drawing the return value
foundCar inside the loop, as in figure 4.19, implies it can indicate both that a matching car
was found and that such a car was not found. How this is done is not shown is the SSD.
37
Chapter 4 Analysis
Figure 4.19 The system sequence diagram, with a loop reflecting the alternative flow.
Common Mistakes
As mentioned above, there is not so much freedom in drawing the system sequence diagram
as there is in drawing the domain model. Many mistakes do not lead to a correct diagram of
less value, but instead to one that is plainly wrong. Before leaving the SSD, it is therefore wise
to make sure that none of the following common mistakes are made.
• Wrong kind of arrow.
• System operation, return value or parameter is missing.
• Operation name does not start with a verb.
• Operation name describes the system’s internal design, for example
searchInDatabase instead of search.
• Entities outside the actor are included. A typical version of this mistake would be to
include an object :Customer in the case study’s SSD.
• The object :System is split into more objects, showing the system’s internal design. NO!
As an example, it would be wrong to include an object :Car, :Rental or :Balance
in the case study.
• Loops or if-statements are not correctly modeled.
• External systems, like :Printer in the case study, are missing.
• To draw activation bars is not wrong, but it is discouraged since it tends to confuse,
rather than clarify.
38
Chapter 5
Design
The purpose of design is to reflect on the code that shall be developed and create a plan that
gives a clear understanding of which classes and methods the code will contain, and how they
will communicate. To write a program without a plan is as inadequate as building a house
without a plan. The created plan, that is the design, shall guarantee that the program becomes
flexible and easy to understand. Flexible means that it shall be possible to add new func-
tionality without having to change existing code, and to change existing functionality without
having to change any code besides that handling the actual functionality being changed. Easy
to understand means that developers not involved in originally creating the program, shall
be able to understand and maintain it, without rewriting anything or destroying the program
structure.
The result of the design is a plan in the form of UML diagrams, illustrating the details of
the program. Before those can be created, this chapter covers the necessary UML. After that,
three concepts are covered, that are necessary requirements for a design that is flexible and
easy to understand. Next comes an introduction to architecture, or, more specifically, how to
organize the program in subsystems. The last thing before doing the design of the RentCar
case study, is to present a step-by-step method for design.
It is not possible to create a design of a program without understanding how the design
can be implemented in code. Make sure you fully understand sections 1.2 and 1.3 before !
reading this chapter.
5.1 UML
This section introduces the UML needed for the design diagrams. Two new diagram types are
introduced, package diagram and communication diagram. Also, more features of class and
sequence diagrams, which where introduced in chapter 4.1, are covered.
39
Chapter 5 Design
Class Diagram
To create a design class diagram, some features are
needed that have not been used previously in this text,
namely methods, visibility and types. Methods are de-
clared in the lowest compartment of the class symbol.
A method parameter’s type is written after the param-
(a)
eter, separated from the parameter by a colon. The
methods return type is written the same way, but af-
ter the entire method, see figure 5.1a. Static methods
(and attributes) are underlined, see figure 5.1b. The
visibility of a class member (method, attribute or any-
(b)
thing else defined in the class) defines an object’s level
of access to that member. For now, only two kinds of
visibility are considered, public and private. Any code
has access to a member with public visibility, while
only code in the declaring class has access to a mem-
ber with private visibility. The symbols + and - are
(c) used in uml to indicate public and private visibility,
respectively, see figure 5.1c.
Figure 5.1 Class diagram, illustrating:
(a) method (b) static members
(c) public and private visibility Package Diagram
The UML symbol package means just a grouping of
something. In a class diagram of a Java program, the
package symbol can mean a Java package. It can also
be used to illustrate a larger grouping, like a subsystem
consisting of many Java packages. Figure 5.2 shows
an example of a package diagram. The dashed line
means that something in somePackage is dependent Figure 5.2 A package diagram
on something in someOtherPackage. The diagram
does say anything about the extent or type of this dependency.
Sequence Diagram
This section explains some previously not covered features, needed to create design sequence
diagrams. Figure 5.3 illustrates some of these. First, the call to firstMethod is a found
message, which is specific in the sense that the caller is unspecified. This is normally used
when the origin of the message is outside the scope of the diagram, and shall not be described
in detail. The scope of the diagram is to describe what happens as a consequence of the call to
firstMethod, not to describe when or why that call is made.
Second, types are depicted as in a class diagram, following the parameter or method, sepa-
rated by a colon.
40
Chapter 5 Design
Figure 5.3 Found message, types, activation bars, message to caller, constructor
Third, note the use of activation bars, which show the duration of a method. The bar begins
on the first line of a method and ends when returning from the method. There can be any num-
ber of activation bars overlapped simultaneously on the same object, since execution might be
inside any number of methods in the same object. For example, firstMethod in the object
someObj in figure 5.3 calls aMethod in otherObj, which in its turn calls someMethod in
someObj. Since at this point firstMethod has not yet returned, execution is inside both
firstMethod and someMethod, illustrated by the double activation bar of someObj.
Fourth, the call to methodInSelf illustrates a call where the caller and callee are the
same object. Also in this case there is a double activation bar, since execution is inside both
firstMethod and methodInSelf.
Last, a constructor call is illustrated with the
creation of newObj. This method must be called
ThirdClass, since a constructor always has the
same name as the class in which it is located.
Also, the return type must be ThirdClass, since
the newly created object of that type is returned
by the constructor. The text «create» above the
constructor call is a stereotype, which tells that
the element with the stereotype belong to a cer-
tain category of such elements. Here, it says that Figure 5.4 Static method and interaction use
the method ThirdClass belongs to the create
category, which means it is a constructor. A stereotype can contain any text, the diagram author
is free to invent new stereotypes. However, there are conventions, for example constructors
have, by convention, the stereotype «create» . It would seem that «constructor» would
be a more logical stereotype, but unfortunately «create» is used instead.
Figure 5.4 illustrates two more features of a sequence diagram. First, met2 is a static
method, which is illustrated with the stereotype «static» . Second, the box labeled ref is
an example of an interaction use. It tells there are more method calls where the box i placed,
those can be seen in a sequence diagram named SomeTask. A diagram should be split like
this when it becomes difficult to understand, or fit in a page, because of its size.
41
Chapter 5 Design
Communication Diagram
A communication diagram serves exactly the same purpose as a sequence diagram, to il-
lustrate a flow of messages between objects. Both these types of diagrams are interaction
diagrams. Which type of interaction diagram to use is completely up to the creator, ev-
erything relevant for design can be illustrated in both types. The advantage of a sequence
diagram is that time is clear, since there is a time
axis (downwards), whereas a communication dia-
gram does not have a time axis but illustrates mes-
sage order by numbering the messages. The ad-
vantage of a communication diagram is that ob-
jects can be added both horizontally and verti-
cally, whereas a sequence diagram has all objects
beside each other and therefore tend to become
very wide.
Figure 5.5 shows a communication diagram. A
caller and callee are connected by a line, called
link. A message (method call) is illustrated by an
arrow along the link and the name of the message
(method), its parameters, types and return value.
The first call, metA, has index 1. If a called is
made from the method metA, it has number 1.1,
as illustrated by metB in figure 5.5. A second call Figure 5.5 A communication diagram
from metA has index 1.2 and so on. The order of
execution in figure 5.5 is thus 1, 1.1, 1.2, 1.2.1,
2, 3, 3.1. If there are more than one messages
between the same pair of objects, they are still connected by only one link, see calls 2, 3 and
3.1. Message 1.2 illustrates a constructor call and message 1.2.1 shows a message where caller
and callee are the same object.
Figure 5.6 illustrates conditional calls in mes-
sage 1 and 2, and iteration in message 2.1. A con-
dition is called guard and is specified in square
brackets. UML does not specify guard syntax,
any text or program statement is allowed. The as-
terisk in message 2.1 indicates iteration. It shall
be placed before the square bracket, *[i=1..n],
but that is unfortunately not possible in astah.
Therefore, it is included in the guard statement,
which is not correct UML.
Figure 5.6 Conditional call and iteration in com-
munication diagram
42
Chapter 5 Design
Encapsulation
Encapsulation means that irrelevant internal details are hidden. In order to use a certain item,
for example a clock, it is not required to under-
stand exactly how it works internally, as in figure
5.7. Instead, it exposes an interface with every-
thing the user has to know. In case of the clock,
this is the current time.
To understand encapsulation in software, the
concept visibility must be clear. The visibility of
a declaration states where that declaration is visi-
ble. For now, it is enough to understand two kinds
of visibility, public and private. Public visibil-
ity, specified in Java with the modifier public,
makes the declaration visible to all parts of the
Figure 5.7 If you would have to understand all the program. Any piece of code, anywhere in the
internals of a clock to tell the time, it would entire program, can use a declaration with pub-
be very bad encapsulation. Image by FreeIm-
ages.com/Colin Adamson
lic visibility, no matter what is declared or where
it is declared. Private visibility, specified in Java
with the modifier private, means the declaration is visible only to code in the class in which
the declaration is placed.
1 public class TheClass {
Encapsulation relies on the difference
2 private int var;
between public interface and implemen-
3
tation. The public interface is code that 4 public TheClass(int var) {
is visible to all other code, that is, the 5 this.var = var;
declarations with public visibility. The 6 }
implementation consists of code not vis- 7
ible to all other code, that is, method 8 public void
bodies and declarations with private vis- 9 doSomething(String s) {
ibility. Something is part of the im- 10 anotherMethod(s);
plementation when access to that some- 11 }
thing can be controlled. It is possible to 12
tell exactly which code can access a cer- 13 private void
tain part of the implementation, it is not 14 anotherMethod(String s) {
required that no other code at all can ac- 15 //Some code
cess it. Also, there is no “neutral” code, 16 }
17 }
all code is either public interface or im-
Listing 5.1 Public interface in blue italic.
43
Chapter 5 Design
plementation. As a first example, consider listing 5.1, where the public interface is marked
with blue italic print. The defining question is if this code is changed, can code anywhere in
the entire program be affected? If the answer is yes, it is part of the public interface. That is
why parameter and return value of the public method is marked in listing 5.1.
Continuing with slightly more complicated examples, consider listing 5.2. The static
modifier of PI is part of the public interface, since PI might be used in a statement like
MyClass.PI. If static is removed, that statement will not work. The status of the modifier
final is quite subtle. If a non-final field is made final, code might definitely break since it
will no longer be allowed to write to that field. If a final field becomes non-final, it is not
obvious that code will break. However, it would be very surprising if a (previously) final field
suddenly changed value. The conclusion of this reasoning is that it is safest to consider the
final modifier to be part of the public interface.
The type and name of PI are of course part of the public interface, but why not the value?
The answer lies in the promise of this field, which is specified in the comment. Whether the
value is 3.14, 3.1416 or has some other precision, it would still fulfill its contract, to be the
constant pi. If the comment had said “The constant pi with two decimals”, the value would
have been part of the public interface. Is this a bit of hairsplitting? Maybe, but then at least it
illustrates how one can reason when identifying the public interface.
The private constructor on line seven is not public interface, what matters is that it is pri-
vate. That it is a constructor is of no importance. Finally, the exception list on line eleven is
definitely part of the public interface. If it is changed, exception handling code might break.
This holds both for checked and unchecked exceptions.
44
Chapter 5 Design
The point in making this distinction between public interface and implementation is that
the implementation can be changed anytime, without any risk of unwanted consequences.
Changing the public interface, on the other hand, is very dangerous since any code anywhere
might break. That might not be a big issue in a small program with only one developer.
However, in programs just slightly bigger, with more than one developer, changing the public
!
interface immediately becomes challenging. Those whose code break will not be very happy,
especially if it happens regularly or without notice. This is even more disastrous if the code
is part of a published API, where it is impossible to know who is using it.
As an example, consider the two methods in listing 5.3. They both have exactly the same
public interface, but implementations differ. It would be no problem at all to change between
the two implementations, code that calls multiplyWithTwo is completely independent of
whether the multiplication is done by straightforward multiplication or by shifting. The same
way, it is without any risk to change other parts of the implementation, for example name or
parameter types of a private method. It could be argued that it is not allowed to change the
implementation of multiplyWithTwo at free will, for example not to return operand
* 5. It is true that such a change can not be made, but that is because it changes the public
interface, since both name and comment become erroneous by such a change.
1 /**
2 * Doubles the operand and returns the result.
3 */
4 public int multiplyWithTwo(int operand) {
5 return operand * 2;
6 }
7
8 /**
9 * Doubles the operand and returns the result.
10 */
11 public int multiplyWithTwo(int operand) {
12 return operand << 1;
13 }
Listing 5.3 Two methods with the same public interface, but different im-
plementations.
In conclusion, it is essential that a public interface is well designed and as small as possible.
As soon as a program grows to any reasonable size, it becomes very difficult to change any
part of its public interface, to say the least. Many programs suffer from strange constructs
originating in public interfaces impossible to change.
45
Chapter 5 Design
High Cohesion
Cohesion is a measurement of how well defined a
class’ knowledge and its tasks are, and how well
they fit together. The goal is that a class shall
represent one single abstraction, which is clearly
identified by the class name. Furthermore, the
class shall have knowledge about that abstraction,
not about anything else, and perform tasks related
to that abstraction, not anything else. When this
important goal is reached, the class has high co-
hesion.
Figure 5.9 shows two different designs of the
Figure 5.8 When there is high cohesion, the parts same program, one with low cohesion (figure
fit together and create a whole that is easy to
understand. Image by unknown creator [Public do- 5.9a) and one with high cohesion (figure 5.9b). In
main], via https://pixabay.com the low cohesion design, the Employee class has
the method getAllEmployees, which returns a
list of all employees. This means an Employee instance, which represents one single em-
ployee, knows all employees in the department. That is not relevant knowledge, instead, that
information fits better in a Department class, which reasonably shall know all employees
working at the department. This latter design, with higher cohesion, is illustrated in figure
5.9b. Also, the Employee in figure 5.9a has a method changeSalaryOfEmployee, which
can change the salary of any employee, not just that particular instance. The better design,
in figure 5.9b, has another version of this method, which means an instance can change only
its own salary. In conclusion, in the better design, an Employee instance knows about, and
performs operations on, only itself, while in the worse design, an instance knows about, and
performs operations on, any instance.
(a)
(b)
46
Chapter 5 Design
Another example is given in figure 5.10, which illustrates a Car class. In the design with
lower cohesion, figure 5.10a, Car has methods and attributes which are more related to the
abstractions radio and engine. In the design with higher cohesion, figure 5.10b, those attributes
and methods are moved to the new, appropriately named, classes Radio and Engine. Of
course, as is the case with all designs, it can be argued that there are problems also with the
designs in figures 5.9b and 5.10b. Still, those two definitely have higher cohesion than their
low cohesion counterparts in figures 5.9a and 5.10a.
(a)
(b)
It is absolutely mandatory to always strive for high cohesion. If not, the program will be
difficult to understand, and also difficult to change, since code that is not really related will
be mixed together in the same class. The programmer can not relax even if the program, at
some point in time, has high cohesion. As more code is added, the program will eventually
!
get low cohesion if classes are not split. Therefore, always be on the guard for the possibility
to improve the design by introducing new classes, with a clearer responsibility.
Finally, although only classes have been discussed in this section, exactly the same rea-
soning applies to programming constructs of all other granularities as well. Also subsystems,
packages, methods and even fields must be continuously scrutinized regarding cohesion.
47
Chapter 5 Design
Low Coupling
Coupling defines to which extent a class depends
on other classes. What is interesting is primarily
on how many other classes it depends, the type
of dependency is not of great interest; whether
method call, parameter, return value or something
else does not matter much. Low coupling means
there are as few dependencies as possible in the
program. It is not possible to tell a maximum al-
lowed number, what matters is that there are no
dependencies which are not required.
The main reason to strive for low coupling
is that if a class (depender) depends on another
Figure 5.11 If the UML diagram looks like a
bowl of spaghetti, there is too high cou- class (dependee), there is a risk that the depender
pling. Image by Katrin Baustmann [Public domain], via must be changed as a consequence of a change in
https://pixabay.com
the dependee. If for example a method name is
changed, also all classes calling that method must
be changed. The problem is bigger the less control the developer has of the dependee. For
example, it is a quite small problem if the program is small and developed by only one person,
but much bigger in a large program where developers far away might change the dependee.
The size of the problem is also defined by the stability of the dependee. The more often a class
is changed, the bigger problem to depend on it. For example, it is completely safe to depend on
classes in the APIs in the JDK, in the java.* packages, since they change extremely seldom.
Figure 5.12 shows two different designs of the same program, one with high coupling
and one with low coupling. In the version with unnecessarily high coupling, figure 5.12a,
HighCouplingOrder has a reference to HighCouplingShippingAddress. This is not
required since Order can get ShippingAddress from Customer. Therefore, this reference
can be omitted, as illustrated in figure 5.12b.
(b)
(a)
Another example of unnecessarily high coupling is found in figure 5.13a, which depicts a
typical “spider in the web” design, with a “spider” class that has references to many other,
peripheral, classes. The peripheral classes in such a design tends to have none or very few ref-
erences to other classes. The problem here is that the spider class normally becomes involved
48
Chapter 5 Design
in all operations, thereby getting messy code with bad cohesion. The peripheral classes, on
the other hand, tends to become just data containers, doing nothing at all, which makes their
purpose unclear. A spider in the web design can normally be improved by moving some of
the peripheral classes further away from the spider class, as is done with Guest and Room in
figure 5.13b. This, improved, design does not have a spider class with references to all other
classes. Also, there is not a huge set of peripheral classes without references. Note that the
total number of references is the same in both designs in figure 5.13. Still, coupling is lowered
since the better design does not include a spider class with high coupling.
(a)
(b)
Just as is the case for high cohesion, low coupling is not something that can be achieved
once and for all. It is absolutely mandatory to always try to minimize the coupling. Also
parallel to high cohesion, low coupling does not apply only to classes, but to programming
constructs of all granularity, for example subsystems, packages and methods.
49
Chapter 5 Design
5.3 Architecture
The architecture gives the big picture of the sys-
tem under development, it shows how the system
is divided into subsystems. It tells which prob-
lems the system can solve, and where in the sys-
tem each problem is solved. It does not, however,
tell exactly how the problem is solved, that be-
longs to design and coding. As an analogy, con-
sider the architectural plan of a building in figure
5.14. It ensures that the problem of moving be-
tween floors can be solved, since there is a stair.
It does not tell exactly how to construct the stair,
which materials to use, etc. And it most certainly
Figure 5.14 An architectural plan does not show is not a real, usable, stair. It is just a plan. Sim-
any details of the construction. Image by Gunnar ilarly, an architectural plan of a software system
Way-Matthiesen (stockholmskällan) [Public domain], via
Wikimedia Commons could ensure that for example data storage can be
handled, by including a database and a class or
package that calls the database. However, the architecture of the software system would not
be a detailed design of the database or the calling package, and it would definitely not be an
actual database or program, but instead a UML diagram or something similar.
Patterns
This section will cover architectural patterns, but first, let us make clear what a pattern is.
A pattern is a common and proven solution to a reoccurring problem. Typically, developers
realize that a particular problem in software development is solved many times, in different
programs, but the solution is always more or less the same. If this solution works well, it is
worth creating a formalized description covering the problem, variants of the solution, advan-
tages and disadvantages of the solution, etc. This formalized description is a pattern. If it
concerns architecture, it is an architectural pattern, if it concerns design it is a design pattern,
and so on. A collection of patterns is like a cookbook for software development. Knowledge
of patterns becomes a common vocabulary for software developers, that can be used to discuss
possible ways to solve a particular problem.
50
Chapter 5 Design
a particular declaration (field, method, class, etc) is visible only to code in the same package
as that declaration. In UML, it is illustrated with the tilde character, see figure 5.15. In Java, it
is declared by omitting visibility modifier, do not write neither public, nor private (or any-
thing else). See appendix C.11, showing the implementation of figure 5.15. Note that package
private visibility is closely related to private visibility. Both are part of the implementation
and impose a strong limit on the visibility. Both make it possible to tell exactly which code
can see the declaration.
51
Chapter 5 Design
52
Chapter 5 Design
Before leaving the MVC pattern, it is worth considering interaction between the three sub-
systems a bit more. Regarding view and controller, a remaining question is who handles flow
control between views. Suppose for example the the user interface shows a list with summary
information about different items. If the user clicks an item, a new view shall be displayed,
with detailed information about the clicked item. Which object knows that the list view shall
be replaced by the detailed view? Flow control is the responsibility of the controller, not
the view, but the controller does not know which view is currently displayed. The answer
53
Chapter 5 Design
is that managing flow control between views is a task complex enough to give low cohesion
to whatever class it is placed in. It is best to introduce a new class, which has exactly this
responsibility. This object could be placed in the controller layer, but note that it is not a
Controller class.
Regarding communication between view and model, it is best that, as in figures 5.18 and
5.19, there is no such communication at all. That makes these two layers are completely
independent, reducing coupling as much as possible. If so, the only way to send data from
model to view, for showing to the user, is as return values to method calls from view, via
controller, to model. If that is possible, everything is fine. Unfortunately, it is often not
possible, since one method call might require many different return values. It might also be
that a view shall be updated when no call to the model has been made, for example as a result
of the model being updated by a call from another program, or because of a timer updating the
model regularly. An option could be to add lots of getter methods to the model, and let the view
use those to retrieve the required data. This solution has some big disadvantages, for example
that corresponding getter methods must be added
to the controller, which will make it terribly
bloated and messy. Also, the view can not know
exactly when to call those getters, since it can not
know when the model changes state, if the state
change is not initiated by the view itself. There
is an elegant solution to this problem, that will be
covered later. For now, all considered scenarios
will allow data to be passed from model to view
as return values to method calls via controller.
54
Chapter 5 Design
layers that have been mentioned so far. Therefore, yet a new layer must be introduced, whose
responsibility is to start the application.
Exactly which layers there shall be in a system is a matter of discussion, and it also differs
from system to system. However, all layers that have been mentioned here are often present.
Those layers are depicted in figure 5.20, they are view, controller, model, dbhandler
(sometimes called integration, responsible for calling the database), data (the actual
database) and startup, which includes the main method and all other code required to start
the application.
Always strive to keep dependencies in the direction illustrated in figure 5.20, that is from
higher (closer to the user) layers to lower (further from the user) layers. Those dependencies
are unavoidable, since execution is initiated by the user, and to call lower layers there must
be dependencies. Dependencies in the opposite direction are, however, not needed. There is
nothing forcing lower layers to call higher layers. Since such dependencies are unnecessary,
introducing them means unnecessarily high coupling. Also, higher layers tend to be less stable
than lower layers. For example, it is more common to change user interface layout than it is
to change business rules, and yet less common than to change those rules is to remove entities
represented in the database.
To conclude, the layer pattern has important advantages. If layers are correctly designed,
they form subsystems with high cohesion and low coupling. Also encapsulation applies to
layers, the public interface of a layer shall be as small as possible, not revealing more than
required of the layers internal workings. When encapsulation, cohesion and coupling are used
to make layers independent, it becomes easy to maintain the layers and to divide development
of different layers between different developers or teams of developers. It is also easy to reuse
code, since a layer can provide a well-designed public interface, callable from any code in a
higher layer.
55
Chapter 5 Design
Listing 5.4 The same method signature often appears in many different layers. This is problematic if the method
has a long parameter list.
Just to make many method calls is not a problem, but the long parameter list is. First, it is
difficult to remember the meaning of each parameter, especially when they all have the same
type, as is the case here. Second, a long parameter list means a large public interface, and
thereby a bigger risk that it is changed. An often used method to get rid of the parameter list
is to use a data transfer object, DTO. Such an object is a just data container, without any logic.
Its only purpose is to group data in the same class, see listing 5.5.
1
2 //The DTO
3 public class UserDTO {
4 private String name;
5 private String streetAddress;
6 private String zipCode;
7 private String city;
8 private String country;
9 private String phone;
10 private String email;
11
12 public UserDTO(String name, String streetAddress, String zipCode,
13 String city, String country, String phone,
14 String email) {
15 this.name = name;
16 this.streetAddress = streetAddress;
17 ...
18 }
19
20 public String getName() {
21 return name;
22 }
23
24 public String getStreetAddress() {
25 return streetAddress;
56
Chapter 5 Design
26 }
27
28 ...
29 }
30
31 //In the controller layer
32 public void registerUser(UserDTO user) {
33 //Call to model
34 }
35
36 //In the model layer
37 public void registerUser(UserDTO user) {
38 //Call to dbhandler
39 }
40
41 //In the dbhandler layer
42 public void registerUser(UserDTO user) {
43 //Call to database
44 }
Listing 5.5 Here, the problematic parameter list of listing 5.4 has been removed by introducing a DTO
An obvious objection is that the long parameter list is not gone, it is just moved to the
constructor of the DTO, UserDTO, on lines 12-14 in listing 5.5. However, it now appears only
in one place. If it is changed, only one public interface is changed, not one in each layer. Also,
it is now obvious that all user related data belongs together.
1. Use the patterns MVC and layer. This means to create one package for each layer
that is supposed to be needed. Exactly which layers that is, is a matter of discussion.
An educated guess that is valid for many programs is to use the layers depicted in figure
5.20. The class Controller can also be created already now. Illustrate the packages
and Controller in a class diagram.
2. Design one system operation at a time. The system sequence diagram shall guide our
design, it shows exactly which input and output the program shall have. Also design
enough of the system’s start sequence (initiated by the main method) to be able to test
run the newly designed system operation. When deciding which classes and methods
57
Chapter 5 Design
there shall be, use interaction diagrams. An interaction diagram shows the flow through
the program, how methods call each other. Whether to use sequence or communication
diagrams is a matter of taste.
3. Strive for encapsulation with a small, well-defined public interface, high cohesion
and low coupling. When adding new functionality, create the required methods in a
way that these goals are met to the highest reasonable degree. This is much easier said
than done, and often requires much thought and discussion. It helps to remember that an
operation shall be placed in a class representing the abstraction to which the operation is
associated, and that has the data required for the operation. The domain model helps to
find new classes that can be introduced. Also, always be prepared to change previously
designed system operations if that improves the overall design.
When designing, favor objects over primitive data and avoid static members, since nei-
ther primitive data nor static members are object oriented. When using these, the entire
object concept is completely ignored, and the prime tool (objects) to handle encapsula-
tion, cohesion and coupling is thrown away.
4. Maintain a class diagram with all classes. When done designing a system operation,
summarize the design in the class diagram created in bullet 1, which will contain all
classes in the program. Such a diagram gives a good overview.
5. Implement the new design in code. Design is not an up front activity that can be done
once and for all for the entire program. Instead, it shall be done in iterations, as soon as
a design is ready it shall be implemented in code, and thus evaluated. Here, this step is
postponed until the next chapter. The reason is that seminar two would otherwise be
far too big. Also, postponing programming allows focusing on design without getting
lost in code. However, when designing, it is important to have an understanding of how
the design can be implemented in code.
6. Start over from bullet 2 and design the next system operation.
58
Chapter 5 Design
59
Chapter 5 Design
5.20. Therefore, after having introduced the Controller class, the design looks as in figure
5.24.
Figure 5.24 The first version of the RentCar design class diagram.
60
Chapter 5 Design
61
Chapter 5 Design
Figure 5.25 The first version of the searchMatchingCar design interaction diagram.
comes. This is illustrated in method call one, where it is created. But what is the origin of the
parameters in method call one? That is explained in the note, they are entered by the user in
the user interface. Last, the diagram does not tell in which layer the Car class is located. It is
quite OK not to show layers in the interaction diagram, but we still must decide the location
of Car. In fact, all layers are candidates, since it already appears in view and controller,
and we can guess that it will be passed through model to dbhandler, since a search in the
database for a matching car is probably required. A rule of thumb is to place a class in the
lowest layer where it is used, in order to avoid dependencies from lower to higher layers. This
would indicate that Car should be placed in dbhandler. However, there are other questions
as well, is Car a DTO or is it the actual model object, the entity, with the business logic?
Also, does the entity (in the model) contain any business logic at all, or has it only got getter
methods? In the latter case, there is no real need to introduce both a DTO and an entity, since
they would be identical. Instead, the Car class in the model could be considered to be a DTO.
These questions can not be answered until more of the system is designed. For now, we just
choose the simplest solution, namely to let Car be a DTO, place it in dbhandler, and not
add an entity. This decision might have to be changed later. Note that if we had decided to
turn Car into an entity object, it could no longer have been created by the view, since model
objects shall only be called by the controller. Let’s change the name to CarDTO to make clear
it is a DTO, this makes the communication diagram look as in figure 5.26.
62
Chapter 5 Design
Next, it is time to decide which object is called by Controller. Shall some object in the
model be created or shall the controller just make a search in the database? The answer is that
a model object shall be created if it is of any use after this call. For example if it later shall be
stored in the database or if it will be used in a future system operation. As far as we know now,
none of these cases apply, there is no future use for a model object representing the search.
Therefore, Controller will just call an object responsible for searching in the database. This
object, let’s call it CarRegistry, will reside in the dbhandler layer, since the purpose of
that layer is exactly this, to call the database. The database itself, represented by the data
layer, would normally be another system, called by the CarRegistry class. Here, however,
there is no database. Thus, instead of calling the database, CarRegistry will just look in an
array of available cars. The final design of searchMatchingCar is in figure 5.27. Note that
this diagram has no notion at all of the loop around the system operation, that is drawn in the
system sequence diagram. This is because the loop condition, noMatchingCar is not visible
in the program, but is completely decided by the cashier (after talking with the customer).
Exactly the same flow, depicted in figure 5.27, is executed again and again, until the customer
is satisfied.
63
Chapter 5 Design
created by main. But not only must they be created, they must also be given references to
each other to be able to call each other. In particular, View calls Controller and must there-
fore have a reference to Controller. Also, Controller calls CarRegistry and must
thus have a reference to CarRegistry. One option is that main creates all three objects and
passes references as needed. Another option is that main creates fewer objects, for example
only View, which in turn creates Controller, which finally creates CarRegistry. Let’s
chose the former alternative, main creates all three objects, see figure 5.28. The other solution
could be problematic if in the future there is the need to create for example a Controller
without a CarRegistry. This might also indicate that the controller gets low cohesion if it
creates CarRegistry.
The last task in the design of searchMatchingCar is to summarize what has been done in
64
Chapter 5 Design
a class diagram, see figure 5.29. Note that it is not mandatory to include all attributes, methods
and references if they do not add any important information, but only obscure the diagram.
For example, it is common not to include references to DTOs, since they are used in many
different layers and are considered as data types. Not including references to DTOs is similar
to not including references to java.lang.String, which can also be considered a data type.
Before leaving searchMatchingCar, we evaluate it according to the criteria encapsula-
tion, cohesion and coupling. To start with encapsulation, all methods are public. This is not
exactly an ideal situation, but often quite unavoidable early in the design. We are creating the
different layers and tying them together. In fact, all methods are called across layer borders,
and it has to be that way. Otherwise, it would not be possible to communicate between lay-
ers, since there are still very few methods. All fields, on the other hand, are private, which
is very good. Looking at cohesion, we can safely say that all classes do what they are meant
for, nothing more. Main starts the program, View just calls the controller, Controller has
a system operation that calls a search method in the CarRegistry. CarRegistry has only
this search method and CarDTO, finally, has no methods at all. Regarding coupling, there is
a chain of dependencies from higher to lower layers, that is from view to controller to
dbhandler, which is exactly the purpose of the layer pattern. Also, it is perfectly in order
to have dependencies from Main to the other layers, since the task of Main is to start the
other layers. It would most likely not be appropriate if Main had references to many classes
in the same layer, that would probably be too high coupling. For the moment, however, Main
references only one class in each layer.
That concludes the design of the searchMatchingCar system operation. The question
naturally arises, whether all this designing is really necessary just to fetch an element from
an array? The answer is yes, most definitely yes. A professional programmer should, and
normally does, make this kind of considerations all the time. However, having gained more
experience by designing more programs, the reasoning made in this section can often be done
quite quickly.
65
Chapter 5 Design
and SSD, CustomerDTO has the attributes name, address and drivingLicense. Shall
these be strings or new classes? In order to shorten the discussion, the domain model is
followed without further consideration. This means name becomes a string, while the other
two becomes new classes, AddressDTO and DrivingLicenseDTO. Now it is possible to
draw a UML diagram illustrating the call of the registerCustomer method, figure 5.30.
We are not done yet, sending a DTO to the controller does not serve any purpose on its own.
Remember that a DTO shall be considered a data type, like int or String. To complete
customer registration, customer data should reasonably be stored somewhere in the model
(or database). This is a good time to add the Rental class, which is a central class in the
domain model. High cohesion is achieved by letting a rental object represent the entire
rental transaction. This object will know which customer performed the rental, which car was
rented, and other facts related to the rental, by having references to appropriate other objects.
The final design of the registerCustomer system operation look as in figure 5.31.
There is no need to add anything to the main method, since all new objects that were
introduced in this system operation are created in the interaction diagram in figure 5.31. These
objects are address, drivingLicense, customer and the unnamed Rental object.
A word of caution before proceeding to the next system operation. There are now three
different DTOs stored in the model, in the Rental object, and there will likely be even more
as we proceed. We have not considered how these are handled in the model. Are they simply
kept or is all data copied to some other object? This question will be left unanswered until the
design is implemented in code. However, it is a problem that the DTO objects are referenced,
and therefore potentially updated, by both view and model. Once view has passed a DTO as
parameter to controller, it should never be updated again by view. To make sure this does
not happen, all fields, and also the classes themselves, shall be final. This makes the DTO
66
Chapter 5 Design
immutable, which means none of its fields can ever change value. There is no UML symbol
for this, it is illustrated with a note in the class diagram, figure 5.32.
Figure 5.31 The Rental class symbolizes the entire rental transaction.
67
Chapter 5 Design
68
Chapter 5 Design
changed, or a new data store must be created. Let’s try to get inspiration from the domain
model, it shows a RentalCatalog and a CarCatalog. This indicates that there should be
different data stores for cars and rentals. It can also be argued that separating these two classes
creates higher cohesion. However, these two are not exactly what we are looking for, in the
DM they contain specifications of rentals and cars, but in the design we are handling particular
instances of rentals and cars. Also, comparing the DM and the design diagrams, it becomes
clear that the design so far contains no rental or car specification stores, is that a problem? This
is again something that should be discussed with the domain expert. But let’s not deviate to
much from the SSD we are implementing now, we will not consider car or rental specifications
here, since they are not mentioned in the specification.
With the cohesion argument, a RentalRegistry is added, this gives higher cohesion than
storing rentals and cars in the same data store. This results in the design in figure 5.33. Re-
member that CarRegistry and RentalRegistry are not the actual databases, but classes
calling the database. There is nothing stopping us from letting both those call the same under-
lying database if that would be appropriate. In this course, we do not implement any database,
so we do not have to consider that problem.
Note that the CarRegistry method that marks the car as beeing booked is called setState
OfBookedCar, and not bookCar. The reason is that booking the car is business logic, which
belongs in the model. The purpose of the integration layer is to access the data store, not to
perform business logic, like booking a car. Therefore, this method just saves a booked state,
without caring about eventual business rules related to performing a booking.
It is a quite interesting decision to let Rental, instead of Controller call setStateOf
BookedCar. The motive is that the controller should not have detailed knowledge about all
details of all system operations. That would lead towards spider-in-the-web design, with the
controller as spider. Now that calls to registers are being made from Rental, it might be
adequate to move more register calls from Controller to Rental. Then there would of
course be the risk that, in the end, Controller does nothing but forward calls to Rental,
which then becomes the spider class. This reasoning is not an unimportant academic exercise,
it is quite common design problems both to have a controller doing all work itself, and to
have a controller doing nothing but forwarding method calls to another class. To shorten this
discussion a bit, the design is kept as in figure 5.33.
69
Chapter 5 Design
The RentalRegistry object is not created in any design diagram, it must therefore be
created when the system is started. Figure 5.34 shows program startup with instantiation of
RentalRegistry added. With this modification, main creates two objects in the dbhandler
layer. This is a warning sign that it might be getting unnecessarily high coupling to that layer.
Also, the dbhandler layer might have a bit bad encapsulation, since it has to reveal the ex-
istence of CarRegistry and RentalRegistry to main. These problems can be solved by
changing that startup design to the one in figure 5.35, where the class RegistryCreator is
responsible for creating the registries, and thereby hides their existence to main. This solution
will be discussed and improved further in chapter 9. For now, we conclude that the design
in any of figures 5.34 or 5.35 can be used, since the problem regarding encapsulation in the
dbhandler layer is not yet very big. But it might grow in the future, if more registries are
added.
The design class diagram, figure 5.36, is now becoming quite big. In order to reduce it, the
DTOs are omitted. Another option would have been to split it into more, smaller diagrams.
This class diagram illustrates the start sequence in figure 5.35, not 5.34. Note that the con-
structors of CarRegistry and RentalRegistry are package private, since they are called
only by RegistryCreator, which is located in the same package as those registries.
70
Chapter 5 Design
71
Chapter 5 Design
Figure 5.38 Payment and CashRegister handling the pay system operation.
Which object in the model shall handle a payment? Rental is the only class that can come
under consideration without completely ruining cohesion. Is it reasonable that Rental shall
prepare the receipt and perform the task listed in bullet 14 in the scenario, namely to update
the balance? The answer must be no, Rental represents a particular rent transaction, it is not
responsible for receipt creation or for maintaining the balance of the cashier’s cash register.
This means a new class must be created. The DM has no really good candidate for this class,
which probably means something was missed when it was created. Possible classes in the DM
are Payment and Cashier. The former is associated to Receipt, which in turn is associated
to Change, and seems to be a good candidate for handling one specific payment. One specific
payment is, however, not related to the balance in a cash register. Therefore, Payment shall
not handle the balance. Looking in the DM, Cashier is the only possible candidate for
72
Chapter 5 Design
handling the balance, but is a balance really an attribute of the cashier that worked at the cash
register where the balance was generated? The answer must be no, which means none of
the classes in the DM can be used. The most reasonable solution seems to be to introduce a
new class CashRegister, representing the cash register that has the particular balance. The
pay design, after introducing the Payment and CashRegister, is depicted in figure 5.38.
The payment class is called CashPayment instead of Payment, because we are anticipating
that future handling of credit card payments will be quite different and therefore be placed
in a different class. The rental that is being paid is passed to calculateCost, call 1.2.1,
since CashPayment will have to ask the payment about information when calculating the
total rental cost.
As a result of pay, a receipt shall be printed on the printer, which, according to the SSD,
is an external system. Calling an external system is normally handled by a specific class,
which represents the external system and handles all communication with it. This class can
be named after the external system, here, it will be called Printer. This class is not the
actual printer, but a representation of the printer in the program being developed. In which
layer shall this class be placed? Actually, it does not fit in any of the current layers without
giving bad cohesion to the layer where it is placed. There are two main options, either to
create a new layer or to extend (and rename) dbhandler to handle interaction with any other
system, so far databases and printers. The former seems like a road to fragmentation of the
system into many small layers, to high coupling with many references, and to less possibility
for encapsulation, given the many small units. The latter option seems to be a road to low
cohesion in dbhandler. With the current knowledge about the system, it is quite impossible
to tell which option is the best. More or less by chance, the latter option is chosen and the
dbhandler layer is renamed to integration, which is a relatively commonly used name
73
Chapter 5 Design
for a layer responsible for interaction with external systems. The resulting pay design can
be seen in figure 5.39. This design is a bit underspecified, for example it is not clear exactly
how Receipt will gather the receipt information. Actually, it is not even clear exactly which
information the receipt shall contain. However, it is clear that what is designed is sufficient to
allow Receipt to gather the information from Rental. The remaining details will be decided
when the design is implemented in code.
Two objects were introduced without being created, namely the Printer and CashRegister
objects, which must therefore be created during startup. Shall Printer be created by Registry
Creator (which must then be renamed), or shall it be created directly by main? Let’s not
include it in RegistryCreator, since, after all, a printer connection is completely different
from a database connection. The RegsitryCreator will be responsible only for connecting
to the database. Perhaps the same connection can be used for both the car and rental registries,
but most certainly not for the printer. This decision gives the final startup design of figure
5.40.
Why is the CashRegister object created by Controller, when all other objects are
created by main and sent to Controller? This is a trade-off between two contradicting
arguments. On one hand, coupling is lowered if main is not associated to all other objects cre-
ated during startup. On the other hand, cohesion of the Controller constructor is increased
if it does not create loads of other objects, besides controller. The design of figure 5.40
balances these arguments. Since Controller is the entry point to model, it makes sense that
74
Chapter 5 Design
it creates the model objects, like CashRegister. The main method creates central objects
in the integration, controller and view layers, but nothing more.
The last thing to do is to draw the design class diagram, which can be seen in figure 5.41.
Note that there is no data layer, it is not needed since there is no database.
75
Chapter 5 Design
Figure 5.42 Improved pay design, with less coupling from Controller.
76
Chapter 5 Design
77
Chapter 5 Design
• The design has a spider-in-the-web class, which is often the controller. The solution is
to remove associations between the spider and some peripheral classes, and instead add
associations between peripheral classes. This has been covered in detail previously.
• Objects are not used sufficiently, instead primitive data is passed in method calls. It is
not forbidden to use primitive data, but always consider introducing objects, especially
if there are long lists of parameters in a method or attributes in a class. Not using objects
means that the prime tool (objects) to handle encapsulation, cohesion and coupling is
thrown away.
• There are unwarranted static methods or fields. It is not forbidden to use static mem-
bers, but there must be a very god reason why there are such. Static members do not
belong to any object, and are therefore, just as is the case for primitive members, us-
ing them means that the prime tool (objects) to handle encapsulation, cohesion and
coupling is thrown away.
• There a too few classes. It is of course very difficult to tell how many classes there
should be in a certain design, but in some cases there are clearly too few. An example
is if the model consists of only one class, which performs all business logic. Cohesion
is the prime criteria used to decide if there is a sufficient number of classes, too few NO!
classes normally means that some existing class(es) has low cohesion.
• Too few layers is perhaps a less disastrous mistake than too few classes, especially
early in the development, when the program is relatively small. Still, if one of the
layers view, controller, model, startup or integration is missing, there
must be a reason why that is the case. If one or more of those layers has another name
is probably no problem, what matters is that they exist.
• There can also be too few methods. This can be discovered by evaluating if existing
methods have high cohesion. A method should have one specific task, explained by
its name. However, there can be too few methods even if all existing methods do have
high cohesion. This is the case if some of the program’s tasks is simply not performed
in any method. If so, the design is not complete and a new method must be introduced,
performing the missing task.
• The MVC pattern might be used the wrong way. Under no circumstance must there be
any form of input or output outside the view.
• Also the layer pattern might be used the wrong way. All layers must have high cohesion
and there should be calls only from higher (closer to the user) layers to lower layers.
78
Chapter 5 Design
• Data appears out of nothing. Always consider if it is possible to implement the design
in code. That is not possible if a certain variable is passed in a method call, but the
variable does not exist in the calling method.
• The class diagram is too big and is therefore unreadable. The diagram might be un-
readable because it is messy, showing many details, or because it has been shrunk to fit
on a printed page, making the text too small. The solution is to either split it in more, NO!
smaller, diagrams, or to remove details that are not needed to understand the design.
Examples of things to remove are DTOs, private members and/or attributes. Remem-
ber that the goal of removing details is to make it easier to understand the diagram, do
not remove things that are required for understanding. After having removed details, it
might become difficult to use the class diagram as a template when coding. However,
there should still be a complete design of each class in the design tool, this can be used
when programming.
79
Chapter 6
Programming
This chapter describes how to implement the design in code, and how to test the code. Coding
is never a straightforward translation of the design diagrams. Most likely there are coding
details that were not considered during design. It is also quite likely to discover actual design
mistakes. This means there is no sharp line between the design and implementation activities,
there will be need for decisions regarding program structure, encapsulation, cohesion, cou-
pling and so on also when coding. In addition to this, there will also be questions regarding
code quality, that are not really design issues, for example how to name variables and how to
write comments in the code.
As soon as a certain functionality is implemented in code, it should be tested. This chapter
covers fundamentals of unit testing. This includes guidelines about what, when and how to
test, and an introduction to technologies that facilitate testing.
80
Chapter 6 Programming
6.3 Comments
There should be one javadoc comment for each declaration that belongs to a public interface.
Javadoc comments start with /** and end with */. These are used to generate html files with
api documentation using the javadoc jdk command. Most IDEs (for example NetBeans and
Eclipse) provide a graphical user interface to the javadoc command.
81
Chapter 6 Programming
The javadoc comment shall describe what the commented unit does, but not how it is done.
How belongs to the implementation, not to the public interface. Method comments shall
explain not only what the method does, but also its parameters and return value. These are
commented using the @param and @return javadoc tags. The <code> tag shall be used for
Java keywords, names and code samples. This is illustrated in listing 6.1. The @param tag is
used on lines 11 and 23, the @return tag on line 24 and the <code> tag on lines 19, 20 and
23. The html file generated with the javadoc command is, in part, depicted in figure 6.1.
1 /**
2 * Represents an amount of money. Instances are immutable.
3 */
4 public final class Amount {
5 private final int amount;
6
7 /**
8 * Creates a new instance, representing the specified
9 * amount.
10 *
11 * @param amount The amount represented by the newly
12 * created instance.
13 */
14 public Amount(int amount) {
15 this.amount = amount;
16 }
17
18 /**
19 * Subtracts the specified <code>Amount</code> from
20 * this object and returns an <code>Amount</code>
21 * instance with the result.
22 *
23 * @param other The <code>Amount</code> to subtract.
24 * @return The result of the subtraction.
25 */
26 public Amount minus(Amount other) {
27 return new Amount(amount - other.amount);
28 }
29 }
It is seldom meaningful to add more comments, inside methods. To make sure such com-
ments are up to date is often burdensome extra work, that far too often is simply not done. If
comments are not maintained, they will not correctly describe the code, which will result in
developers not trusting the comments. Low trust in comments is a very unproductive state of
a program. It means both that the commenting work was in vain, and that unnecessary time is
spent reading code instead of comments. Therefore, avoid placing comments inside methods.
82
Chapter 6 Programming
Figure 6.1 Part of the javadoc generated from the code in listing 6.1.
Instead, the need for comments inside a method should be seen as a signal that the method is
too long, and ought to be split into shorter methods. More on this below.
83
Chapter 6 Programming
Novice programmers reveal their lack of knowledge by writing code that has several code
smells. It is relatively common for employers to test the ability to find such problems in a
piece of code when hiring new programmers.
Duplicated Code
Identical code in more than one place in the program is a really bad smell. It means whenever
that piece of code shall be changed, exactly the same editing must be done in all locations
where the duplicated code exists. This is of course inefficient since more writing is needed,
but far worse is that it is easy to miss one or more code locations, which means the code will
not work as expected after the (incomplete) change is made. This will lead to long and boring
searches for lines in the program where the duplicated code was not changed as intended.
How sensitive to duplicated code shall one be? The answer is very sensitive! The goal must
always be that not a single statement shall be repeated anywhere in the program. Allowing
duplicated code is to enter a road that leads to disaster. Duplicated code is normally introduced
by copying previously written code. You should hear a loud warning bell ring if you ever type
ctrl-c ctrl-v when programming.
As an example, consider the code in listing 6.2, where the printout of the contents of the
names array is duplicated. In fact, also the javadoc comment to the three methods is dupli-
cated. Duplicated comments introduce exactly the same complications as duplicated code.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * This class has bad smell since it contains duplicated code.
5 * The duplicated code is the loop printing the contents of
6 * the <code>names</code> array.
7 */
8 public class ClassWithDuplicatedCode {
9 private String[] names;
10
11 /**
12 * To perform its task, this method has to print the
13 * contents of the <code>names</code> array.
14 */
15 public void aMethodThatShowsNames() {
16 //some code.
17 for (String name : names) {
18 System.out.println(name);
19 }
20 //some code.
21 }
22
23 /**
24 * To perform its task, this method has to print the
84
Chapter 6 Programming
Listing 6.2 The loop with the printout of the names array is duplicated.
Suppose the printout in listing 6.2 has to be modified, say that lines 18, 30 and 42 shall
be changed to System.out.println("name: " + name);. This change has to be per-
formed on all three lines. Also, as mentioned above, the fact that there is duplicated code
makes it quite difficult to be sure all lines where the code exists where actually changed,
especially if the program is large.
This smell is removed by using the refactoring Extract Method, which means to move code
from an existing method into a new method, which contains this particular code. In the current
example, it is the printout loop that shall be placed in the newly created method. This new
method is then called on all lines where the printout is required. Listing 6.3 shows the code
after applying this refactoring. Here, there is no duplicated code, the desired change is done
by editing only line 43.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * This class does not contain duplicated code. The
5 * previously duplicated code has been extracted to
6 * the method <code>printNames</code>
7 */
8 public class ClassWithoutDuplicatedCode {
9 private String[] names;
85
Chapter 6 Programming
10
11 /**
12 * To perform its task, this method has to print the
13 * contents of the <code>names</code> array.
14 */
15 public void aMethodThatShowsNames() {
16 //some code.
17 printNames();
18 //some code.
19 }
20
21 /**
22 * To perform its task, this method has to print the
23 * contents of the <code>names</code> array.
24 */
25 public void otherMethodThatShowsNames() {
26 //some code.
27 printNames();
28 //some code.
29 }
30
31 /**
32 * To perform its task, this method has to print the
33 * contents of the <code>names</code> array.
34 */
35 public void thirdMethodThatShowsNames() {
36 //some code.
37 printNames();
38 //some code.
39 }
40
41 private void printNames() {
42 for (String name : names) {
43 System.out.println("name: " + name);
44 }
45 }
46 }
Listing 6.3 When the Extract Method refactoring has been applied, the loop with the
printout of the names array is no longer duplicated.
Listing 6.4 shows a more subtle example of duplicated code. The problem here is the code
sequence[1], which is used to access the first element in the array sequence. This code is
wrong, since the first element is located at index zero, not one. To fix this bug, both lines 16
and 23 must be changed. The situation would be even worse in a larger program, where the
indexing mistake would occur on numerous lines. Just as in the previous example, the solution
is to extract a method containing the duplicated code, see listing 6.5.
86
Chapter 6 Programming
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * This class has bad smell since it contains duplicated code.
5 * The duplicated code is <code>sequence[1]</code> to access
6 * the first element in the <code>sequence</code> array.
7 */
8 public class ClassWithUnobviousDuplicatedCode {
9 private int[] sequence;
10
11 /**
12 * @return <code>true</code> if the the specified value is
13 * equal to the first element in the sequence.
14 */
15 public boolean startsWith(int value) {
16 return sequence[1] == value;
17 }
18
19 /**
20 * @return The first element in the sequence array.
21 */
22 public int getFirstElement() {
23 return sequence[1];
24 }
25 }
Listing 6.4 The duplicated code in this class is the usage of sequence[1] to access the first
element in the array
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * This class does not contain duplicated code. The previously
5 * duplicated code has been extracted to the method
6 * <code>firstElement</code>
7 */
8 public class ClassWithoutUnobviousDuplicatedCode {
9 private int[] sequence;
10
11 /**
12 * @return <code>true</code> if the the specified value is
13 * equal to the first element in the sequence.
14 */
15 public boolean startsWith(int value) {
16 return firstElement() == value;
17 }
87
Chapter 6 Programming
18
19 /**
20 * @return The first element in the sequence array.
21 */
22 public int getFirstElement() {
23 return firstElement();
24 }
25
26 private int firstElement() {
27 return sequence[0];
28 }
29 }
Listing 6.5 The introduction of the method firstElement has removed the duplicated code.
All occurrences of the duplicated code were located in the same class in both examples
above. This is certainly not always the case, the same code might just as well exist in different
classes. Also in this case, the solution is to extract a method with the duplicated code, and
replace all occurrences of that code with calls to the newly created method. The specific issue
when multiple classes are involved, is where to place the new method. One option is to place
it in one of the classes that had the duplicated code, another option is to place it in a new class.
In either case, the classes that do not contain the new method, must call the new method in the
class where it is placed. The best placement must be decided in each specific case, based on
how cohesion, coupling and encapsulation are affected by the different alternatives.
Long Method
It is easier to understand the code if all methods have names that clearly explain what the
method does. A guideline for deciding if a method is too long is does the method name tell
everything that is needed to fully understand the method body? If there seems to be need for
comments inside a method, that is clearly not the case. In fact, comments or need of comments
inside a method is a clear sign that the method is too long. Thus, what matters is not primarily
the number of lines in a method, but how easy it is to understand the method body. Consider
the method in listing 6.6, which is quite short but still not easy to understand. What is the
meaning of the numbers 65 and 90 on line 13? The answer is that the ASCII numbers of upper
case letters are between 65 and 90. This becomes clear if a new method, with an explaining
name, is introduced, see listing 6.7. To introduce a new method with an explaining name is
almost always the best way to shorten and explain a method that is too long.
1 /**
2 * Counts the number of upper case letters in the specified
3 * string.
4 *
5 * @param source The string in which uppercase letters are
6 * counted.
88
Chapter 6 Programming
Listing 6.6 In spite of the few lines, this method is too long since it is not clear what line 13
does.
1 /**
2 * Counts the number of upper case letters in the specified
3 * string.
4 *
5 * @param source The string in which uppercase letters are
6 * counted.
7 * @return The number of uppercase letters in the specified
8 * string.
9 */
10 public int countUpperCaseLetters(String source) {
11 int noOfUpperCaseLetters = 0;
12 for (char letter : source.toCharArray()) {
13 if (isUpperCaseLetter(letter)) {
14 noOfUpperCaseLetters++;
15 }
16 }
17 return noOfUpperCaseLetters;
18 }
19
20 private boolean isUpperCaseLetter(char letter) {
21 return letter >= 65 && letter <= 90;
22 }
Listing 6.7 Here, each method body is explained by the method’s name.
It is sometimes argued that the program becomes slower if there are many method calls.
This is simply not true, to perform a method call is not significantly slower than any other
statement. Trying to decrease execution time by minimizing the number of method calls is not
any smarter than trying to minimize the number of statements in the program.
89
Chapter 6 Programming
Large Class
Just as is the case for methods, whether a class is too large is not primarily decided by the
number of lines. The main criteria is instead cohesion, a class is too large if it has bad cohesion.
Cohesion was covered extensively above, in section 5.2. The class in listing 6.8 shows that
cohesion can be improved also by splitting small classes. This listing contains the Meeting
class, which represents a meeting in a calendar. It has the fields startTime and endTime
that together define the meeting’s duration. These two fields are more closely related to each
other, than to other fields in the class. The fact that they have a common suffix, Time, helps us
see this. Cohesion is improved in listing 6.9, by extracting a class with these two fields. This
is a quite common way to realize that a new class is appropriate. As programming continues,
the new class will probably get more fields and methods.
1 package se.kth.ict.oodbook.prog.smell;
2
3 import java.time.LocalDateTime;
4
5 /**
6 * This class represents a meeting in a calendar.
7 */
8 public class MeetingLowerCohesion {
9 private LocalDateTime startTime;
10 private LocalDateTime endTime;
11 private String name;
12 private boolean alarmIsSet;
13
14 //More fields and methods.
15 }
Listing 6.8 This class has two fields that are more closely related than other
fields. This is an indication that cohesion can be improved by extracting a
new class, with these fields.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * This class represents a meeting in a calendar.
5 */
6 public class MeetingHigherCohesion {
7 private TimePeriod period;
8 private String name;
9 private boolean alarmIsSet;
10
11 //More fields and methods.
12 }
13
90
Chapter 6 Programming
14
15 package se.kth.ict.oodbook.prog.smell;
16
17 import java.time.LocalDateTime;
18
19 /**
20 * Represents a period in time, with specific start
21 * and end time.
22 */
23 class TimePeriod {
24 private LocalDateTime startTime;
25 private LocalDateTime endTime;
26
27 //More fields and methods.
28 }
Listing 6.9 Here, cohesion is improved by moving the related fields to a new
class.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * This class represents a person. The call to
5 * <code>dbHandler</code> does not preserve the
6 * <code>Person</code> object. The fields are instead passed as
7 * primitive parameters.
8 */
9 public class PersonObjectNotPreserved {
91
Chapter 6 Programming
Listing 6.10 The call to dbHandler does not preserve the Person object. The fields are instead
passed as primitive parameters.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * This class represents a person. The call
5 * to <code>dbHandler</code> preserves the
6 * <code>Person</code> object.
7 */
8 public class PersonObjectPreserved {
9 private String name;
10 private String address;
11 private String phone;
12
13 /**
14 * Saves this <code>Person</code> to the specified
15 * database.
16 *
17 * @param dbHandler The database handler used to save the
18 * <code>Person</code>.
19 */
20 public void savePerson(DBHandler dbHandler) {
21 dbHandler.savePerson(this);
22 }
23 }
1 package se.kth.ict.oodbook.prog.smell;
92
Chapter 6 Programming
2
3 /**
4 * This class represents a bank account. The
5 * <code>deposit</code> method takes primitive parameters
6 * instead of using a parameter object.
7 */
8 public class AccountWithoutParameterObject {
9 /**
10 * Adds the specified amount of the specified currency to
11 * the balance.
12 *
13 * @param currency The currency of the deposited amount.
14 * @param amount The amount to deposit.
15 */
16 public void deposit(String currency, int amount) {
17 }
18 }
Listing 6.12 The deposit method takes primitive parameters instead of using a parameter ob-
ject.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * This class represents a bank account. The parameters of the
5 * <code>deposit</code> method are encapsulated in an object.
6 */
7 public class AccountWithParameterObject {
8 /**
9 * Adds the specified amount of the specified currency to
10 * the balance.
11 *
12 * @param currency The currency of the deposited amount.
13 * @param amount The amount to deposit.
14 */
15 public void deposit(Amount amount) {
16 }
17 }
18
19 package se.kth.ict.oodbook.prog.smell;
20
21 /**
22 * Represents an amount
23 */
24 class Amount {
25 private String currency;
93
Chapter 6 Programming
Listing 6.13 The Amount class has been created and encapsulates the parameters of the deposit
method.
In some cases, there are parameters that are simply not needed, because the called method
itself can find the data by making a request to an object it already knows. This refactoring
is called Replace Parameter With Method, and is illustrated in listing 6.14, which has the
unnecessary parameter (lines 19 and 39), and listing 6.15, where the called method gets the
data instead of using a parameter (lines 18 and 39).
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * The bank application’s controller. The call to
5 * <code>withdraw</code> passes the <code>fee</code> parameter
6 * that is not needed.
7 */
8 public class ControllerPassingExtraParameter {
9 private AccountWithExtraParameter account;
10 private AccountCatalog accts;
11
12 /**
13 * Withdraws the specified amount.
14 *
15 * @param amount The amount to withdraw.
16 */
17 public void withdraw(Amount amount) {
18 Amount fee = accts.getWithDrawalFeeOfAccount(account);
19 account.withdraw(amount, fee);
20 }
21 }
22
23 package se.kth.ict.oodbook.prog.smell;
24
25 /**
26 * Represents a bank account. The method <code>withdraw</code>
27 * takes the <code>fee</code> parameter that is not needed.
28 */
29 public class AccountWithExtraParameter {
30 // Needed for some unknown purpose.
31 private AccountCatalog acctSpecs;
32
33 /**
94
Chapter 6 Programming
Listing 6.14 The call to withdraw passes the fee parameter that is not needed.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * The bank application’s controller. The call to
5 * <code>withdraw</code> does not pass the
6 * <code>fee</code> parameter.
7 */
8 public class ControllerNotPassingExtraParameter {
9 private AccountWithoutExtraParameter account;
10 private AccountCatalog accts;
11
12 /**
13 * Withdraws the specified amount.
14 *
15 * @param amount The amount to withdraw.
16 */
17 public void withdraw(Amount amount) {
18 account.withdraw(amount);
19 }
20 }
21
22 package se.kth.ict.oodbook.prog.smell;
23
24 /**
25 * Represents a bank account. The <code>fee</code> parameter
26 * is not passed to <code>withdraw</code>, since it can be
27 * retrieved in that method itself.
28 */
29 public class AccountWithoutExtraParameter {
30 // Needed for some unknown purpose, besides getting the
31 // withdrawal fee.
32 private AccountCatalog acctSpecs;
33
34 /**
35 * Withdraws the specified amount.
95
Chapter 6 Programming
36 *
37 * @param amount The amount to withdraw.
38 */
39 public void withdraw(Amount amount) {
40 acctSpecs.getWithDrawalFeeOfAccount(this);
41 }
42 }
Listing 6.15 The call to withdraw does not pass the fee parameter, since it is retrieved by the
withdraw method.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * Represents a person. This class has excessive primitive
5 * data, since it has fields that fit better as an object.
6 */
7 public class PersonManyFields {
8 private String name;
9 private String street;
10 private int zip;
11 private String city;
12 private String phone;
13 private String email;
14
15 // More code in the class.
16 }
Listing 6.16 This class has excessive primitive data, since it has fields that fit better as an object.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
96
Chapter 6 Programming
Listing 6.17 This Person class uses objects for the fields.
It might be that a class has not only fields, but field(s) and one or more methods that are
closer related than other fields and methods in the class. Also in this case, cohesion can be
improved by introducing a new class. The new class shall contain the field(s) and methods of
the original class that belong closely together. This refactoring is called Replace Data Value
With Object. The code before the applying the refactoring is listed in listing 6.18. It shows the
class Person, which has the pnr field (line 10) that holds a person number. The class also
has the method validatePnr (line 17), that checks if the control digit of the person number
is correct. This method really belongs to the pnr field, not to the Person class. Cohesion is
improved in listing 6.19, by introducing the PersonNumber class, which shall always be used
to represent person numbers. Note that validatePnr (line 30) is called in the constructor of
PersonNumber (line 26). That way, there can never exist any invalid person numbers in the
program, they are immediately revealed when a PersonNumber is created.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * Represents a person. This class has low cohesion since the
5 * method <code>validatePnr</code> belongs to the
6 * <code>pnr</code> field, rather than to this class.
7 */
97
Chapter 6 Programming
Listing 6.18 This class has low cohesion since the method validatePnr belongs to the pnr
field, rather than to this class.
1 package se.kth.ict.oodbook.prog.smell;
2
3 /**
4 * Represents a person. The method <code>validatePnr</code>
5 * has been moved to <code>PersonNumber</code>.
6 */
7 public class PersonWithPnrClass {
8 private String name;
9 private PersonNumber pnr;
10
11 public PersonWithPnrClass(String name, PersonNumber pnr) {
12 this.name = name;
13 this.pnr = pnr;
14 }
15 }
16
17 package se.kth.ict.oodbook.prog.smell;
18
19 /**
20 * Represents a person number.
21 */
22 public class PersonNumber {
23 private String pnr;
24
25 public PersonNumber(String pnr) {
26 validatePnr(pnr);
27 this.pnr = pnr;
28 }
29
30 private void validatePnr(String pnr) {
98
Chapter 6 Programming
31 }
32 }
A far too common mistake is to use an array of primitive data instead of an object. If arrays
are used correctly, all elements in the same array means the same thing. It is not correct if
different elements in an array means different things, as is the case with the stats array in
listing 6.20. In this code, there is absolutely nothing showing that the first element is the name
of a football team, the second the number of wins, the third the number of draws and the fourth
the number of losses. This information exists only in the mind of the developer, and it is of
course easy to confuse the meaning of the array positions. The code has been improved in
listing 6.21, where an object is used instead of the array. The meaning of each value is now
clear from the names of the fields in the object.
99
Chapter 6 Programming
Many programming languages, including Java, has enumerations. This enables defining a
custom type and the possible values of that type. As an example, consider listing 6.22 that
does not use an enumerator. Instead, the possible results of the call to connect are strings.
With such code, nasty bugs can appear because of misspelling the result string. An equally
bad alternative is to use integers for the outcomes of connect. In this case, bugs might appear
bescause of confusing which number means what. A much better alternative is to introduce a
new type for the outcomes, using an enumeration. This is illustrated in listing 6.23. The new
type, ResultCode, can take the values SUCCESS, PENDING and FAILURE. The meaning of
each outcome is obvious from the value and misspelled values will generate a compiler error.
100
Chapter 6 Programming
Meaningless Names
This is not mentioned as a particular smell in [FOW], but should still be avoided at all costs.
Everything that is declared in a program (packages, classes, interfaces, methods, fields, pa-
rameters, local variables, etc) must have a meaningful name. This can not be stressed enough.
The following list provides a few naming guidelines.
• Do not use one-letter identifiers like Person p = new Person();, instead write
Person person = Person();. There are two exceptions to this guideline. The
first is when the full name of the abstraction is just one letter long, it is for example
appropriate to use the identifier x for an x coordinate. The second exception is that a
one letter identifier is accepted for a loop variable, which is normally named i. Nested
loops are named using following letters in alphabetical order, j, k, etc.
• Do not name a temporarily used variable tmp or temp (unless it represents a tempera-
ture). For example do not swap two values as in listing 6.24, instead use variable names
like in listing 6.25.
• Do not be afraid of long names, what matters is that the identifier correctly explain
the purpose of what is named. Say for example that some reward is given to the first
customer buying a particular item in some campaign in a shop. An adequate name for a
variable holding that customer could be firstCustomerBuyingCampaignItem.
101
Chapter 6 Programming
Unnamed Values
This is not mentioned as a particular smell in [FOW], but should still be avoided at all costs.
All values in a program shall have an explaining name. Never introduce a value in a statement
without naming it first, even if that statement is the only place the value is used. Without a
name it can be very hard to understand the purpose of the value. Naming the value is a better
practice than writing a comment to explain it. A name is part of the program, the compiler
helps to ensure that the name is used correctly. A comment, on the other hand, is a kind of
duplicated information that exists besides the program. There is always the risk that comments
are not maintained when the program changes. Below is a list with some examples of naming
values.
• Say that the method connect(int timeout) tries to connect for timeout number
of milliseconds before stopping. Say also that we want to make it try for ten sec-
onds. A straightforward way to write this could be connect(10000), but then the
reader gets no information about the purpose of the value 10000. A better way is to
write connect(10 * MILLIS_PER_SECOND), and defining the constant private
static final int MILLIS_PER_SECOND = 1000;. Still, however, the purpose
of the value 10 might not be clear. The best way to code this is to also define a constant
private static final int CONNECT_TIMEOUT_SECS = 10;, or, if it is not a
constant, the variable int connectTimeoutSecs = 10;. Now, the code becomes
connect(CONNECT_TIMEOUT_SECS * MILLIS_PER_SECOND) or
connect(connectTimeoutSecs * MILLIS_PER_SECOND).
• The practice of naming values applies (at least) to all primitive types, and also to strings,
since a string can be written as a primitive value, without using the keyword new. Con-
sider for example opening a file, whose name is in the variable fileName, located in
the directory whose name is in the variable dirName. Assuming that there is a method
openFile, that opens a file, this might be done with the statement openFile(dirName
+ "\" + fileName);. The meaning of the value "\" might seem clear, still, it is
even clearer to introduce the constant private static final String
PATH_SEPARATOR = "\";, and write openFile(dirName + PATH_SEPARATOR
+ fileName);. Using this constant everywhere a path separator is needed also gives
the advantage that it is easy to change path separator, if running on a system where the
path separator is not a backslash. Using the constant, only one line has to be changed,
the declaration of the constant.
• It is not required to use a variable or constant to name the value, sometimes a method
suits better. This is often the case when naming values that occur in if statements. As
an example, consider an if statement checking for end of line (EOL) in a string. EOL
in a unix file is represented by a character with ASCII code 10, therefore, the code in
listing 6.26 might be used. This code is unclear, why check for the value 10? A better
solution is to introduce the method isUnixEol, and use the code in figure 6.27.
102
Chapter 6 Programming
1 /**
2 * Finds the index of the first Unix EOL in the specified
3 * string.
4 *
5 * @param source The string in which to look for EOL.
6 * @return The index of the first EOL, or -1 if there was
7 * no EOL in the specified string.
8 */
9 public int findIndexOfFirstEolWorse(String source) {
10 char[] sourceChars = source.toCharArray();
11 for (int i = 0; i < sourceChars.length; i++) {
12 if (sourceChars[i] == 10) {
13 return i;
14 }
15 }
16 return -1;
17 }
Listing 6.26 It is quite difficult to understand the meaning of an unnamed value, like the
value 10 on line 12
Listing 6.27 The purpose of the value 10 on line 2 is explained by the name of the method,
isUnixEol
103
Chapter 6 Programming
1 package se.kth.ict.rentcar.view;
2
3 import se.kth.ict.rentcar.controller.Controller;
4 import se.kth.ict.rentcar.integration.CarDTO;
5
6 /**
7 * This program has no view, instead, this class is a
104
Chapter 6 Programming
Listing 6.28 The class View when only the searchMatchingCar system operation has
been implemented.
1 package se.kth.ict.rentcar.controller;
2
3 import se.kth.ict.rentcar.integration.CarRegistry;
105
Chapter 6 Programming
4 import se.kth.ict.rentcar.integration.CarDTO;
5 import se.kth.ict.rentcar.integration.RegistryCreator;
6
7 /**
8 * This is the application’s only controller class. All
9 * calls to the model pass through here.
10 */
11
12 public class Controller {
13 private CarRegistry carRegistry;
14
15 /**
16 * Creates a new instance.
17 *
18 * @param regCreator Used to get all classes that
19 * handle database calls.
20 */
21 public Controller(RegistryCreator regCreator) {
22 this.carRegistry = regCreator.getCarRegistry();
23 }
24
25 /**
26 * Search for a car matching the specified search criteria.
27 *
28 * @param searchedCar This object contains the search
29 * criteria. Fields in the object that
30 * are set to <code>null</code> or
31 * <code>0</code> are ignored.
32 * @return The best match of the search criteria.
33 */
34 public CarDTO searchMatchingCar(CarDTO searchedCar) {
35 return carRegistry.findCar(searchedCar);
36 }
37 }
Listing 6.29 The class Controller when only the searchMatchingCar system operation has
been implemented.
1 package se.kth.ict.rentcar.integration;
2 /**
3 * Contains information about one particular car.
4 */
5 public final class CarDTO {
6 private final int price;
7 private final String size;
8 private final boolean AC;
106
Chapter 6 Programming
107
Chapter 6 Programming
55 return false;
56 }
57 if (searched.getColor() != null &&
58 !searched.getColor().equals(color)) {
59 return false;
60 }
61 if (searched.isAC() != AC) {
62 return false;
63 }
64 if (searched.isFourWD() != fourWD) {
65 return false;
66 }
67 return true;
68 }
69
70 @Override
71 public String toString() {
72 StringBuilder builder = new StringBuilder();
73 builder.append("regNo: " + regNo + ", ");
74 builder.append("size: " + size + ", ");
75 builder.append("price: " + price + ", ");
76 builder.append("AC: " + AC + ", ");
77 builder.append("4WD: " + fourWD + ", ");
78 builder.append("color: " + color);
79 return builder.toString();
80 }
81
82 // Getters are not listed.
83
84 }
Listing 6.30 The class CarDTO when only the searchMatchingCar system operation has been
implemented.
1 package se.kth.ict.rentcar.integration;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * Contains all calls to the data store with cars that may be
8 * rented.
9 */
10 public class CarRegistry {
11 private List<CarDTO> cars = new ArrayList<>();
12
108
Chapter 6 Programming
13 CarRegistry() {
14 addCars();
15 }
16
17 /**
18 * Search for a car matching the specified search criteria.
19 *
20 * @param searchedCar This object contains the search
21 * criteria. Fields in the object that
22 are set to <code>null</code> or
23 * <code>0</code> are ignored.
24 * @return <code>true</code> if a car with the same
25 * features as <code>searchedCar</code> was found,
26 * <code>false</code> if no such car was found.
27 */
28 public CarDTO findCar(CarDTO searchedCar) {
29 for (CarDTO car : cars) {
30 if (car.matches(searchedCar)) {
31 return car;
32 }
33 }
34 return null;
35 }
36
37 private void addCars() {
38 cars.add(new CarDTO(1000, "medium", true, true, "red",
39 "abc123"));
40 cars.add(new CarDTO(2000, "large", false, true, "blue",
41 "abc124"));
42 cars.add(new CarDTO(500, "medium", false, false, "red",
43 "abc125"));
44 }
45 }
Listing 6.31 The class CarRegistry when only the searchMatchingCar system operation
has been implemented.
1 package se.kth.ict.rentcar.integration;
2 /**
3 * This class is responsible for instantiating all registries.
4 */
5 public class RegistryCreator {
6 private CarRegistry carRegistry = new CarRegistry();
7
8 /**
9 * Get the value of carRegistry
109
Chapter 6 Programming
10 *
11 * @return the value of carRegistry
12 */
13 public CarRegistry getCarRegistry() {
14 return carRegistry;
15 }
16 }
Listing 6.32 The class RegistryCreator when only the searchMatchingCar system op-
eration has been implemented.
1 package se.kth.ict.rentcar.startup;
2
3 import se.kth.ict.rentcar.controller.Controller;
4 import se.kth.ict.rentcar.integration.RegistryCreator;
5 import se.kth.ict.rentcar.view.View;
6
7 /**
8 * Contains the <code>main</code> method. Performs all startup
9 * of the application.
10 */
11 public class Main {
12 /**
13 * Starts the application.
14 *
15 * @param args The application does not take any command
16 * line parameters.
17 */
18 public static void main(String[] args) {
19 RegistryCreator creator = new RegistryCreator();
20 Controller contr = new Controller(creator);
21 new View(contr).sampleExecution();
22 }
23 }
Listing 6.33 The class Main when only the searchMatchingCar system operation has been
implemented.
110
Chapter 6 Programming
itself and all its fields are final. If DTOs had not been final, it would have been suicide to just
keep them in Rental. In that case, the object that sent the DTO to Rental could have kept a
reference to the same DTO object, and later updated it.
There is also another issue with keeping the DTOs. Is it really sure they are just DTOs,
having no logic at all? If, for example, there is the need to validate the driving license number,
or to calculate a person’s age based on the driving license number, the methods performing this
would, with the argument of cohesion, be placed in DrivingLicenseDTO or CustomerDTO.
This would turn that object into an entity object, with business logic, instead of a DTO. This
change is not just a matter of renaming, e.g., from CustomerDTO to Customer, but also
concerns how the object is handled. A DTO may be used in the view, but an entity object may
not. To conclude this discussion, it is obvious that att objects named DTO are, at least for the
moment, DTOs. Therefore, it is perfectly safe to leave them like that now, but we must be
aware that this might have to be changed in the future.
Rental is listed in listing 6.34, to illustrate the reasoning above. The rest of the register
Customer implementation can be found in the accompanying NetBeans project [Code].
1 package se.kth.ict.rentcar.model;
2
3 /**
4 * Represents one particular rental transaction, where one
5 * particular car is rented by one particular customer.
6 */
7 public class Rental {
8 private CustomerDTO customer;
9
10 /**
11 * Creates a new instance, representing a rental made by
12 * the specified customer.
13 *
14 * @param customer The renting customer.
15 */
16 public Rental(CustomerDTO customer) {
17 this.customer = customer;
18 }
19 }
Listing 6.34 The class Rental, after implementing the registerCustomer system opera-
tion.
111
Chapter 6 Programming
the implementation of CarRegistry. That class is supposed to call a database or some other
system that stores car data persistently. Such a datastore does not hold a list of immutable
DTOs, but instead raw, mutable data. This data shall not be object-oriented, since it mimics
a store with primitive data. Instead of having methods, objects shall have only primitive vari-
ables. Therefore, the list in CarRegistry is changed, to hold objects of a class CarData,
which has just primitive fields, no methods at all. This class shall not be used anywhere out-
side CarRegistry, since it mimics the contents of the CarRegistry datastore. To ensure it
is not used anywhere else, it is a private inner class, see lines 97-117 in listing 6.35.
1 package se.kth.ict.rentcar.integration;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * Contains all calls to the data store with cars that may be
8 * rented.
9 */
10 public class CarRegistry {
11 private List<CarData> cars = new ArrayList<>();
12
13 CarRegistry() {
14 addCars();
15 }
16
17 /**
18 * Search for a car matching the specified search criteria.
19 *
20 * @param searchedCar This object contains the search
21 * criteria. Fields in the object
22 * that are set to <code>null</code>
23 * or <code>0</code> are ignored.
24 * @return <code>true</code> if a car with the same
25 * features as <code>searchedCar</code> was found,
26 * <code>false</code> if no such car was found.
27 */
28 public CarDTO findAvailableCar(CarDTO searchedCar) {
29 for (CarData car : cars) {
30 if (matches(car, searchedCar) && !car.booked) {
31 return new CarDTO(car.regNo, car.price,
32 car.size, car.AC,
33 car.fourWD, car.color);
34 }
35 }
36 return null;
37 }
112
Chapter 6 Programming
38
39 /**
40 * If there is an existing car with the registration
41 * number of the specified car, set its booked
42 * property to the specified value. Nothing is changed
43 * if the car’s booked property already had the specified
44 * value.
45 *
46 * @param car The car that shall be marked as
47 * booked.
48 * @param bookedState The new value of the booked property.
49 */
50 public void setBookedStateOfCar(CarDTO car,
51 boolean bookedState) {
52 CarData carToBook = findCarByRegNo(car);
53 carToBook.booked = bookedState;
54 }
55
56 private void addCars() {
57 cars.add(new CarData("abc123", 1000, "medium", true,
58 true, "red"));
59 cars.add(new CarData("abc124", 2000, "large", false,
60 true, "blue"));
61 cars.add(new CarData("abc125", 500, "medium", false,
62 false, "red"));
63 }
64
65 private boolean matches(CarData found, CarDTO searched) {
66 if (searched.getPrice() != 0 &&
67 searched.getPrice() != found.price) {
68 return false;
69 }
70 if (searched.getSize() != null &&
71 !searched.getSize().equals(found.size)) {
72 return false;
73 }
74 if (searched.getColor() != null &&
75 !searched.getColor().equals(
76 found.color)) {
77 return false;
78 }
79 if (searched.isAC() != found.AC) {
80 return false;
81 }
82 if (searched.isFourWD() != found.fourWD) {
83 return false;
113
Chapter 6 Programming
84 }
85 return true;
86 }
87
88 private CarData findCarByRegNo(CarDTO searchedCar) {
89 for (CarData car : cars) {
90 if (car.regNo.equals(searchedCar.getRegNo())) {
91 return car;
92 }
93 }
94 return null;
95 }
96
97 private static class CarData {
98 private String regNo;
99 private int price;
100 private String size;
101 private boolean AC;
102 private boolean fourWD;
103 private String color;
104 private boolean booked;
105
106 public CarData(String regNo, int price, String size,
107 boolean AC, boolean fourWD,
108 String color) {
109 this.regNo = regNo;
110 this.price = price;
111 this.size = size;
112 this.AC = AC;
113 this.fourWD = fourWD;
114 this.color = color;
115 this.booked = false;
116 }
117 }
118 }
Listing 6.35 The class CarRegistry, after implementing the bookCar system operation.
With the above change to CarRegistry, it becomes necessary to change the CarDTO
method matches, which compares the features the customer wishes with the features of an
available car. It must now compare fields in a CarDTO with fields in a CarData, and the latter
must not be used outside CarRegistry. This is solved by removing matches from CarDTO
and instead making it a private method in CarRegistry, see lines 65-86 in listing 6.35. This
is in fact a better location for matches. First, the total public interface decreases since a pub-
lic method becomes private. Second, it was never a very good idea to have such a method in
a DTO. A DTO shall not have any business logic, and matches can be regarded as business
114
Chapter 6 Programming
logic, it performs a matching algorithm and is not just a simple data comparison.
Since a booked car can not be rented by other customers, the findCar method must not
return a booked car, even if it matches the search criteria. This results in the if-statement
on line 30 in listing 6.35. To clarify this new behavior, the method name is changed to
findAvailableCar.
Another refactoring was to change the order of the parameters in the CarDTO constructor,
regNo is now the first parameter. This was done because every time that constructor was
called, the first thought was to place regNo first. This is a clear sign that this is a more logical
ordering of the parameters.
Also the Rental constructor had to be changed, according to figure 5.36, to include a refer-
ence to CarRegistry. This is needed since Rental will call the method setBookedState
OfCar in CarRegistry.
There is one unhandled issue left, what happens if the car that shall be booked is already
booked? This situation is not handled yet, but should be addressed before the program is
completed. That concludes the implementation of bookCar, the rest of the code can be down-
loaded in the accompanying NetBeans project [Code].
115
Chapter 6 Programming
tance and possible discounts. It is CashPayment who has this knowledge about what data is
needed, from where to get it, and how to use it to calculate the total cost.
1 package se.leiflindback.oodbook.rentcar.model;
2
3 import java.time.LocalDateTime;
4
5 /**
6 * The receipt of a rental
7 */
8 public class Receipt {
9 private final Rental rental;
10
11 /**
12 * Creates a new instance.
13 *
14 * @param rental The rental proved by this receipt.
15 */
16 Receipt(Rental rental) {
17 this.rental = rental;
18 }
19
20 /**
21 * Creates a well-formatted string with the entire content
22 * of the receipt.
23 *
24 * @return The well-formatted receipt string.
25 */
26 public String createReceiptString() {
27 StringBuilder builder = new StringBuilder();
28 appendLine(builder, "Car Rental");
29 endSection(builder);
30
31 LocalDateTime rentalTime = LocalDateTime.now();
32 builder.append("Rental time: ");
33 appendLine(builder, rentalTime.toString());
34 endSection(builder);
35
36 builder.append("Rented car: ");
37 appendLine(builder, rental.getRentedCar().getRegNo());
38 builder.append("Cost: ");
39 appendLine(builder, rental.getPayment().
40 getTotalCost().toString());
41 builder.append("Change: ");
42 appendLine(builder, rental.getPayment().
43 getChange().toString());
116
Chapter 6 Programming
44 endSection(builder);
45
46 return builder.toString();
47 }
48
49 private void appendLine(StringBuilder builder,
50 String line) {
51 builder.append(line);
52 builder.append("\n");
53 }
54
55 private void endSection(StringBuilder builder) {
56 builder.append("\n");
57 }
58 }
Listing 6.36 The class Receipt, after implementing the pay system operation.
1 package se.kth.ict.oodbook.rentcar.model;
2
3 /**
4 * Represents one specific payment for one specific rental. The
5 * rental is payed with cash.
6 */
7 public class CashPayment {
8 private Amount paidAmt;
9 private Amount totalCost;
10
11 /**
12 * Creates a new instance. The customer handed over the
13 * specified amount.
14 *
15 * @param paidAmt The amount of cash that was handed over
16 * by the customer.
17 */
18 public CashPayment(Amount paidAmt) {
19 this.paidAmt = paidAmt;
20 }
21
22 /**
23 * Calculates the total cost of the specified rental.
24 *
25 * @param paidRental The rental for which the customer is
26 * paying.
27 */
28 void calculateTotalCost(Rental paidRental) {
117
Chapter 6 Programming
29 totalCost = paidRental.getRentedCar().getPrice();
30 }
31
32 /**
33 * @return The total cost of the rental that was paid.
34 */
35 Amount getTotalCost() {
36 return totalCost;
37 }
38
39 /**
40 * @return The amount of change the customer shall have.
41 */
42 Amount getChange() {
43 return paidAmt.minus(totalCost);
44 }
45 }
Listing 6.37 The class CashPayment, after implementing the pay system operation.
• Incomplete comments. Each public declaration (class, method, etc) shall have a
javadoc comment. Method comments shall cover parameters and return values, us-
ing the javadoc tags @param and @return. It is often argued that it is unnecessary
to comment getter and setter methods. That might very well be the case, but how long
does it take to add a one line comment to a getter or setter? It might even be that the
IDE can generate the comment. If every public declaration has a comment, there is no
risk of missing to comment something by mistake, or by pure laziness.
• Excessive comments. There should be no comments besides the above mentioned NO!
javadoc comments. If there is a need for more comments to explain the code, it proba-
bly means the code is too complex, and has low cohesion.
• Comments written too late. Write the comments together with the code that is com-
mented, maybe even before. That way, writing the comment makes it necessary to
clarify what the code shall do, before (or immediately after) it is written. Also, if com-
ments are written together with the code, they will be of use in future development
of the program. If comments are written last, when the program is already working,
commenting is just a burden, and probably quite a heavy burden.
118
Chapter 6 Programming
• Many of the common design mistakes can be introduced when programming, even if
they were avoided during design. For example, there is the risk to use primitive data
instead of objects, to use static declarations when they are not appropriate or to place
input or output outside the view. See the text on common design mistakes in section
5.6 for more details on this.
NO!
• Section 6.4, on code smell and refactorings, covers many things that shall be avoided
when coding. Maybe the most common of those possible mistakes are meaningless
names and unnamed values.
119
Chapter 7
Testing
How is it possible to know if a program works?
The answer to that question makes a very big dif-
ference. If it is complicated to verify that the pro-
gram works as intended, developers will be ex-
tremely reluctant to make changes. They will nei-
ther be willing to apply refactorings to improve
the design of existing code, nor to change existing
functionality. Instead they will argue against cus-
tomer’s requirement changes, and solve all prob-
lems by adding new code. This is a disastrous
state of development, characterized by fear, un-
certainty and doubt. Because of the reluctance to
work with existing code, developers will have lit-
Figure 7.1 Lack of tests will bring fear, uncertainty
and doubt, since programmers can not trust the tle knowledge about the code and the code will be
program. Image by unknown creator [Public domain], in bad state. This will make them even more re-
via https://pixabay.com
luctant to make changes, which will lead to even
less knowledge and worsen code state even more. This is exactly the opposite of the flexibility
we want to achieve. The code will constantly become less flexible.
If, on the other hand, it is very easy to ver-
ify that the code works as intended, developers
will be happy to change it. They will constantly
improve its design with refactorings. They will
also be glad to improve customer satisfaction by
adjusting to changing requirements. This is the
flexibility of well-designed software! Code qual-
ity constantly improves, developers gets better
knowledge about the code, and thereby becomes
even more willing to change it.
The difference between the two scenarios
above is automated tests. There should be a test
program which gives input to the program under Figure 7.2 Complete tests will bring confi-
test, and also evaluates the output. If a test passes, dence, since programmers can trust the pro-
gram. Image by unknown creator [Public domain], via
the test program does not do anything. If a test https://pixabay.com
120
Chapter 7 Testing
fails, it prints an informative message about the failure. With extensive tests that cover all, or
most, possible execution paths through the program with all, or most, possible variable val-
ues, it is guaranteed that the program works if all tests pass. This is a very good situation, one
command starts the test, which tells if the program under test works or, if not, exactly which
problems there are.
1 @Test
2 public void testEqual() {
3 int amount = 3;
4 Amount instance = new Amount(amount);
5 Amount other = new Amount(amount);
6 boolean expResult = true;
7 boolean result = instance.equals(other);
8 assertEquals(
9 "Amount instances with same states are not equal.",
10 expResult, result);
11 }
121
Chapter 7 Testing
Frameworks
There are many frameworks that facilitate unit testing, since it is an extremely common testing
approach. JUnit was one of the first, and is also very frequently used. But why use a framework
at all? And exactly what is a framework? A framework provides some functionality that is
not specific for a particular applica-
tion, but is needed in different ap-
plications. Think of the Java APIs
from Oracle, they provide function-
ality for common tasks, and can
be used in many different appli-
cations. In contrast to an API, a
framework not only provides code,
but also flow control. This means
the main method is in the frame-
work, not in application code writ-
ten by application developers. The
framework is responsible for call-
ing application code at the right Figure 7.3 The application fits in the framework like a piece in a
time. This fact, that the application puzzle. Execution, the colored lines, enter the application via
is relieved of flow control responsi- method calls from the framework.
bility, is very important. Consider
for example a framework providing some security control. It would be very hard for applica-
tion developers to always remember, and never forget, to call the framework in all necessary
places. Just one miss would introduce a security hole. If, instead, the framework itself has
the main method and is responsible for when to handle security, application code will be com-
pletely relieved of everything related to security control. This is illustrated in figure 7.3, where
the blue piece, representing the application, is placed inside the framework, represented by all
the white pieces. The colored lines are different executions through the program. Execution
may start in a main method inside the framework, as is the case for the black line. Execution
may also enter the framework from the outside, for example via a network call, as is the case
for the red and green lines. But execution never starts in the application. When the colored
lines enter the application piece, it typically means the framework has called a method in the
application. When the lines exit the application piece, that method has returned.
There are many good reasons to use a framework whenever one can be found. First, a
framework is thoroughly tested and proven to work well. If it did not work well, it would
not be used. Second, if there are many developers using the same framework, there will be
lots of documentation, and it will be easy to get help. Third, the fact that the framework is
responsible for flow control makes sure all code is executed in correct order. Last, not using a
framework means writing new code, which means introducing new bugs.
122
Chapter 7 Testing
JUnit
JUnit[JU] is one of the most popular unit testing frameworks for Java. It is based on anno-
tations. An annotation is a part of a Java program that is not executed, but instead provides
information about the program for the compiler, or for the JVM, or, as is the case here, for a
framework (JUnit). An annotation is usually used for properties unrelated to the functionality
of the source code, for example to configure security, networking, multithreading or testing.
It starts with the at sign, @, for example @SomeAnnotation. It may take parameters, for
example @SomeAnnotation(someString = "abc", someBoolean = true). When
writing tests with JUnit, annotations are used to specify the content of methods in the test
code, for example that a certain method contains a test. Some of the most common JUnit
annotations are explained in table 7.1
A fully automated test must not only call the SUT, but also evaluate if the result of the call
is the expected, that is, if the test passed or failed. This evaluation is done with assert methods
in JUnit. An assert method verifies that its parameters meet some contraint, for example that
they are equal. If the constraint is met, the test passes and nothing is printed to the console. If
the parameters do not meet the constraint, the test fails and the specified explaining message
is printed. Some of the most common assert methods are explained in table 7.2.
With this knowledge about frameworks and JUnit, we can understand the first example in
listing 7.2 in more detail. The complete test class is listed in listing 7.3.
123
Chapter 7 Testing
1 package se.kth.ict.oodbook.tests.firstexample;
2
3 import org.junit.After;
4 import org.junit.Before;
5 import org.junit.Test;
6 import static org.junit.Assert.*;
7
8 public class AmountTest {
9 private Amount amtNoArgConstr;
10 private Amount amtWithAmtThree;
11
12 @Before
13 public void setUp() {
14 amtNoArgConstr = new Amount();
15 amtWithAmtThree = new Amount(3);
16 }
17
18 @After
19 public void tearDown() {
20 amtNoArgConstr = null;
21 amtWithAmtThree = null;
22 }
23
24 @Test
25 public void testEqualsNull() {
124
Chapter 7 Testing
125
Chapter 7 Testing
Listing 7.3 Complete unit test for the equals method in listing 7.1
On line 12 in listing 7.3, the setUp method is annotated @Before. This means it is exe-
cuted before each test method. That way, each test is performed on the two new Amount ob-
jects created in setUp. In a similar way, the tearDown method, which is annotated @After
on line 18, is executed after each test method. That way, the Amount instances on which the
test was performed are dropped, and will not be used for any more test. Each method contain-
ing a test is annotated @Test, see lines 24, 33, 43, 54, and 65. Each of these methods will be
called by JUnit when the tests are executed. All test methods follow the same pattern. First,
they set up the test creating required objects. Second, they define the expected result of the
call to the SUT. Third, the SUT is called and the actual result is saved. Finally, the expected
and actual results are evaluated to check if the test passed. This is a very typical layout of a
test method, but there are other alternatives, as we will see below.
126
Chapter 7 Testing
Do not over-design There is no need to design or document test code as thoroughly as the
product that is tested. Allow a certain amount of hacking when writing tests. In test
code, we can play around a bit and write some of those funny and interesting hacks that
never really seem to fit in production code.
Testing takes time, but it is worth that time A rough estimate, which is true remarkably of-
ten, is that test code has about the same length as the tested code, and takes about the
same time to write. This means it is quite time consuming to write tests. However, it
is also true remarkably often that once the tests are in place, they give immediate return
on the time invested in writing them. This return comes as confidence that the code is
really working, and that it will be easy to verify that it is still working if we have to
make changes.
Independent and self-evaluating To get highest possible value from the tests, they shall be
quick and easy to execute. This means they shall start with one command (or one click in
an IDE), no complex manual setup shall be required. Also, they must be self-evaluating.
Either a test passes, and prints nothing, or it fails and gives a short informative message
about the failure. It must not be required to manually evaluate return values from calls
to the SUT. Finally, all tests must be independent. Do not rely on them being executed
in a specific order, or on previous tests having passed. All test executions must give the
same result.
127
Chapter 7 Testing
What to test? Test public, protected and package private code, but not private. A private
method can not be tested, since it can not be called from the test class. Also, if methods
with all other accessibilities work, also private methods work. It is normally not needed
to test setters or constructors that only save values, nor getters that only return a value.
Unless bugs appear, we can take for granted that such methods work as intended. Tests
shall cover as much as possible of the code in the SUT. Try to cover all branches of
if statements. Also, try to test boundary conditions and extreme parameter values, like
null, zero, negative values, objects of wrong type etc. It is also important to test that
a method fails the correct way if illegal parameter values are given, or if some other
precondition is not met.
Never worsen SUT design! Try to never, under any condition, worsen the design of the SUT
just to enable testing. This is a slightly controversial statement. It is often suggested,
for example, to break encapsulation by adding get methods to enable retrieving the state
of an object. The only purpose of breaking encapsulation would be to verify that the
state is correct after a method in the object is tested. This is, however, practically never
necessary. Using hard work, a lot of fantasy, and pragmatically testing more than one
method together, we can almost always write tests without worsening SUT design. A
method must have some effect somewhere, otherwise it is useless. To test it, we just
have to find a way to dig out that effect. As an example, consider a class that can create,
read, update and delete values in some storage that can not be accessed by test code.
These methods can be tested together, for example create a value, read it, and check that
the read value equals the created value. Then create a value, delete it and verify that
it can not be read. The create, update, read, etc. More on testing in difficult situations
follows below.
128
Chapter 7 Testing
SUT. It is quite common to write a large amount of test code in order to create files,
databases, etc required for testing. Whatever test structure is created, must be deleted
after the test is executed. Remember that tests must be completely independent and
repeatable. A test must leave no traces of its execution.
Hard to read output It might be that no usable result is returned by the tested method, nor
is there any getter that can be used to read the result. In this situation, do never write
a getter to facilitate testing, since it breaks encapsulation of the SUT. Fact is that the
SUT must update something somewhere, or it would be useless. Maybe the problematic
method can not be tested alone, but there is often some combination of method calls that
will show if the test passed. This reasoning is expanded above, in the paragraph labeled
Never worsen SUT design in section 7.2.
129
Chapter 7 Testing
To generate a new test class in NetBeans, right-click the project and choose New → Test
for Existing Class.... This will display the New Test For Existing Class di-
alog, which is depicted in figure 7.8. Click the Browse... button, marked with a red cir-
cle, to choose for which class tests shall be generated. In this example, the chosen class is
CarRegistry, from the rent car case study. Last, click Finish, and NetBeans will generate
test code similar to listing 7.4.
Figure 7.8 NetBeans’ New Test For Existing Class dialog. The Browse... button, marked with a
red circle, is used to decide for which class tests shall be generated.
1 package se.kth.ict.oodbook.rentcar.integration;
2
3 import org.junit.After;
4 import org.junit.AfterClass;
5 import org.junit.Before;
6 import org.junit.BeforeClass;
7 import org.junit.Test;
8 import static org.junit.Assert.*;
9
10 public class CarRegistryTest {
11 @BeforeClass
12 public static void setUpClass() {
130
Chapter 7 Testing
13 }
14
15 @AfterClass
16 public static void tearDownClass() {
17 }
18
19 @Before
20 public void setUp() {
21 }
22
23 @After
24 public void tearDown() {
25 }
26
27 @Test
28 public void testFindAvailableCar() {
29 System.out.println("findAvailableCar");
30 CarDTO searchedCar = null;
31 CarRegistry instance = new CarRegistry();
32 CarDTO expResult = null;
33 CarDTO result = instance.findAvailableCar(searchedCar);
34 assertEquals(expResult, result);
35 // TODO review the generated test code and remove the
36 // default call to fail.
37 fail("The test case is a prototype.");
38 }
39
40 @Test
41 public void testSetBookedStateOfCar() {
42 System.out.println("setBookedStateOfCar");
43 CarDTO car = null;
44 CarRegistry instance = new CarRegistry();
45 instance.setBookedStateOfCar(car, false);
46 // TODO review the generated test code and remove the
47 // default call to fail.
48 fail("The test case is a prototype.");
49 }
50 }
The class has the same name as the tested class, but with Test appended to the
class name (line 10). All four before and after methods are generated (lines 11-
25), but they are empty. If some code is needed to prepare a test or to clean up
after a test, it shall be added here. Methods that remain empty can be removed.
One test method is generated for each public, protected or package private method in
131
Chapter 7 Testing
the SUT (lines 27-49). These methods contain a printout (lines 29 and 42), which
should be removed since tests are not supposed to produce any output if they pass.
After this, the test methods create an instance
of the SUT (lines 31 and 44) and of other
objects that are necessary to perform the test
(lines 30 and 43). There is no guarantee these
objects are created correctly, always check if
changes are required. Next, the tested method
is called and the result is saved in a vari-
able (lines 33 and 45). Then an assertion is
called to evaluate the test result (line 34). The
testSetBookedStateOfCar method contains
no assertion, since setBookedStateOfCar is
void. In this case, NetBeans does not know how
to evaluate the outcome. Again. even if the asser-
tion is generated, there is no guarantee it is cor-
rect. Finally, there is a TODO comment and a call
to fail (lines 35-37 and 46-48), which should
both be removed when the test is completed.
To execute the tests, right-click the NetBeans
project and chose Test, as depicted in figure 7.9.
The test result will be displayed in a window sim-
ilar to figure 7.10. Currently, none of the two
Figure 7.9 To run the tests, right-click the Net-
auto-generated tests, testFindAvailableCar
Beans project and chose Test. and testSetBookedStateOfCar, are imple-
mented. As a result, they both fail.
Figure 7.10 NetBeans’ test result window. The auto-generated code always makes a test fail.
132
Chapter 7 Testing
1 /**
2 * Two <code>Amount</code>s are equal if they represent the
3 * same amount.
4 *
5 * @param other The <code>Amount</code> to compare with this
6 * amount.
7 * @return <code>true</code> if the specified amount is equal
8 * to this amount, <code>false</code> if it is not.
9 */
10 @Override
11 public boolean equals(Object other) {
12 if (other == null | | !(other instanceof Amount)) {
13 return false;
14 }
15 Amount otherAmount = (Amount) other;
16 return amount == otherAmount.amount;
17 }
133
Chapter 7 Testing
To take the branch other == null, on line 12, the method must be called with a
null parameter. To take the !(other instanceof Amount) branch, also on line 12,
the parameter must be an object that is not an instance of Amount. An easy choice
is to use a java.lang.Object instance. Finally, there are two different executions
of line 16, one where amount == otherAmount.amount and one where amount !=
otherAmount.amount. These tests can be found in listing 7.6.
1 package se.kth.ict.oodbook.rentcar.model;
2
3 import org.junit.After;
4 import org.junit.Before;
5 import org.junit.Test;
6 import static org.junit.Assert.*;
7
8 public class AmountTest {
9 private Amount amtNoArgConstr;
10 private Amount amtWithAmtThree;
11
12 @Before
13 public void setUp() {
14 amtNoArgConstr = new Amount();
15 amtWithAmtThree = new Amount(3);
16 }
17
18 @After
19 public void tearDown() {
20 amtNoArgConstr = null;
21 amtWithAmtThree = null;
22 }
23
24 @Test
25 public void testEqualsNull() {
26 Object other = null;
27 boolean expResult = false;
28 boolean result = amtNoArgConstr.equals(other);
29 assertEquals("Amount instance equal to null.",
30 expResult, result);
31 }
32
33 @Test
34 public void testEqualsJavaLangObject() {
35 Object other = new Object();
36 boolean expResult = false;
37 boolean result = amtNoArgConstr.equals(other);
38 assertEquals("Amount instance equal to " +
39 "java.lang.Object instance.",
134
Chapter 7 Testing
40 expResult, result);
41 }
42
43 @Test
44 public void testNotEqualNoArgConstr() {
45 int amountOfOther = 2;
46 Amount other = new Amount(amountOfOther);
47 boolean expResult = false;
48 boolean result = amtNoArgConstr.equals(other);
49 assertEquals("Amount instances with different states" +
50 " are equal.", expResult, result);
51 }
52
53 @Test
54 public void testNotEqual() {
55 int amountOfOther = 2;
56 Amount other = new Amount(amountOfOther);
57 boolean expResult = false;
58 boolean result = amtWithAmtThree.equals(other);
59 assertEquals("Amount instances with different states" +
60 " are equal.", expResult, result);
61 }
62
63 @Test
64 public void testEqual() {
65 int amountOfOther = 3;
66 Amount other = new Amount(amountOfOther);
67 boolean expResult = true;
68 boolean result = amtWithAmtThree.equals(other);
69 assertEquals("Amount instances with same states are" +
70 " not equal.", expResult, result);
71 }
72 }
Listing 7.6 Tests for all possible paths through the equals method of the Amount class
Next thing to look for is extreme values of the parameters. All obvious extreme values,
like null, are already covered. However, since this is our first test, we might be extra careful
and test also with an Amount object representing zero. That way, also the default constructor
will be executed in a test. Theoretically, we could test with Amounts representing positive,
negative, Integer.MAX_VALUE and Integer.MIN_VALUE amounts, but there is really no
reason to suspect that the method would behave differently for such values. Listing 7.7 shows
the test for an Amount with the value zero.
135
Chapter 7 Testing
1 @Test
2 public void testEqualNoArgConstr() {
3 int amountOfOther = 0;
4 Amount other = new Amount(amountOfOther);
5 boolean expResult = true;
6 boolean result = amtNoArgConstr.equals(other);
7 assertEquals("Amount instances with same states are" +
8 " not equal.", expResult, result);
9 }
Listing 7.7 Test for the equals method of an Amount representing the amount zero.
Finally, is there any way the parameter can have an illegal value, or is there some precon-
dition that must be met for method to work properly? The answer is “no, the method should
function the same way for all possible parameter values”. That means we are done testing it.
Remember to run the tests and check that they all pass, figure 7.11. Our first green bar!!
The other Amount methods, namely minus, plus and toString, are independent, neither
any of the methods, nor its test, will use any of the other methods. Therefore, they can be
written in any order. Let’s start with minus, which is listed in listing 7.8.
1 /**
2 * Subtracts the specified <code>Amount</code> from this
3 * object and returns an <code>Amount</code> instance with
4 * the result.
5 *
6 * @param other The <code>Amount</code> to subtract.
7 * @return The result of the subtraction.
8 */
9 public Amount minus(Amount other) {
10 return new Amount(amount - other.amount);
11 }
136
Chapter 7 Testing
This method has only one execution path, since there are no flow control statements. There
are no illegal parameter values, but the subtraction may overflow. If, for example, -1 is sub-
tracted from Integer.Min_VALUE, the result is a negative integer with a magnitude too big
to fit in an int. In fact, we have discovered a flaw in the design. The method ought to check
if an overflow occurred, and, if so, throw an exception. However, since exception handling is
covered later in a later chapter, this check is not introduced here. Instead, an explaining text
is added to the javadoc comment, saying that The operation will overflow if the
result is smaller than <code>Integer.MIN_VALUE</code>. What can then be
tested regarding overflow? Nothing in fact, the method might fail, but the failure is not han-
dled in any way. The conclusion is that one test would probably be enough, just perform a
subtraction and check that the result is correct. However, since we have just started, let’s be a
bit overambitious and test positive, negative and zero results, see listing 7.9. Once the first test
for minus is written, it takes about thirty seconds to add the other two, and the more tests that
pass, the greater the pleasure to see them pass. Note that assertEquals, which is called on
lines 10, 23 and 35, will use the equals method in Amount to verify that the two specified
Amount instances are equal. This is why it was important to know that equals worked when
minus was tested. It is now clear that if a test for minus fails, it is because of a bug in minus,
not in equals.
1 @Test
2 public void testMinus() {
3 int amountOfOperand1 = 10;
4 int amountOfOperand2 = 3;
5 Amount operand1 = new Amount(amountOfOperand1);
6 Amount operand2 = new Amount(amountOfOperand2);
7 Amount expResult = new Amount(amountOfOperand1 -
8 amountOfOperand2);
9 Amount result = operand1.minus(operand2);
10 assertEquals("Wrong subtraction result",
11 expResult, result);
12 }
13
14 @Test
15 public void testMinusNegResult() {
16 int amountOfOperand1 = 3;
17 int amountOfOperand2 = 10;
18 Amount operand1 = new Amount(amountOfOperand1);
19 Amount operand2 = new Amount(amountOfOperand2);
20 Amount expResult = new Amount(amountOfOperand1 -
21 amountOfOperand2);
22 Amount result = operand1.minus(operand2);
23 assertEquals("Wrong subtraction result",
24 expResult, result);
25 }
26 @Test
137
Chapter 7 Testing
Listing 7.9 The tests for the minus method of the Amount class
The tests for plus are created exactly the same way as the tests for minus, and are therefore
not covered here. Finally, there is the toString method, which returns a string representa-
tion of the amount, listing 7.10. Also this method is tested with positive, negative, and zero
amounts, see listing 7.11. That concludes testing Amount. There are 15 tests in total, and all
pass, brilliant!
1 @Override
2 public String toString() {
3 return Integer.toString(amount);
4 }
Listing 7.10 The toString method of the
Amount class
1 @Test
2 public void toStringPosAmt() {
3 int representedAmt = 10;
4 Amount amount = new Amount(representedAmt);
5 String expResult = Integer.toString(representedAmt);
6 String result = amount.toString();
7 assertEquals("Wrong string returned by toString",
8 expResult, result);
9 }
10
11 @Test
12 public void toStringNegAmt() {
13 int representedAmt = -10;
14 Amount amount = new Amount(representedAmt);
15 String expResult = Integer.toString(representedAmt);
16 String result = amount.toString();
17 assertEquals("Wrong string returned by toString",
18 expResult, result);
19 }
138
Chapter 7 Testing
20
21 @Test
22 public void toStringZeroAmt() {
23 int representedAmt = 0;
24 Amount amount = new Amount(representedAmt);
25 String expResult = Integer.toString(representedAmt);
26 String result = amount.toString();
27 assertEquals("Wrong string returned by toString",
28 expResult, result);
29 }
Listing 7.11 The tests for the toString method of the Amount class
1 /**
2 * Search for a car matching the specified search criteria.
3 *
4 * @param searchedCar This object contains the search criteria.
5 * Fields in the object that are set to
6 * <code>null</code> or <code>0</code> are
7 * ignored.
8 * @return <code>true</code> if a car with the same features
9 * as <code>searchedCar</code> was found,
139
Chapter 7 Testing
1 @Test
2 public void testSetBookedStateOfCar() {
3 CarDTO bookedCar = new CarDTO("abc123", new Amount(1000),
4 "medium", true, true, "red");
5 CarRegistry instance = new CarRegistry();
6 instance.setBookedStateOfCar(bookedCar, true);
7 CarDTO expResult = null;
8 CarDTO result = instance.findAvailableCar(bookedCar);
9 assertEquals("Booked car was found", expResult, result);
10 }
Listing 7.13 The test for the setBookedStateOfCar method of the CarRegistry class
140
Chapter 7 Testing
1 /**
2 * Creates a well-formatted string with the entire content of
3 * the receipt.
4 *
5 * @return The well-formatted receipt string.
6 */
7 public String createReceiptString() {
8 StringBuilder builder = new StringBuilder();
9 appendLine(builder, "Car Rental");
10 endSection(builder);
11
12 LocalDateTime rentalTime = LocalDateTime.now();
13 builder.append("Rental time: ");
14 appendLine(builder, rentalTime.toString());
15 endSection(builder);
16
17 builder.append("Rented car: ");
141
Chapter 7 Testing
18 appendLine(builder, rental.getRentedCar().getRegNo());
19 builder.append("Cost: ");
20 appendLine(builder, rental.getPayment().getTotalCost().
21 toString());
22 builder.append("Change: ");
23 appendLine(builder, rental.getPayment().getChange().
24 toString());
25 endSection(builder);
26
27 return builder.toString();
28 }
29
30 private void appendLine(StringBuilder builder, String line) {
31 builder.append(line);
32 builder.append("\n");
33 }
34
35 private void endSection(StringBuilder builder) {
36 builder.append("\n");
37 }
Listing 7.14 The createReceiptString method of the Receipt class, and its private
helper methods.
1 @Test
2 public void testCreateReceiptString() {
3 Amount price = new Amount(100);
4 String regNo = "abc123";
5 String size = "medium";
6 boolean AC = true;
7 boolean fourWD = true;
8 String color = "red";
9 CarDTO rentedCar = new CarDTO(regNo, price, size, AC,
10 fourWD, color);
11 Amount paidAmt = new Amount(500);
12 CashPayment payment = new CashPayment(paidAmt);
13 Rental paidRental = new Rental(null, new RegistryCreator().
14 getCarRegistry());
15 paidRental.setRentedCar(rentedCar);
16 paidRental.pay(payment);
17 Receipt instance = new Receipt(paidRental);
18 LocalDateTime rentalTime = LocalDateTime.now();
19 String expResult = "\n\nRented car: " + regNo +
20 "\nCost: " + price +
21 "\nChange: " + paidAmt.minus(price) +
22 "\n\n";
142
Chapter 7 Testing
Listing 7.15 The test for the createReceiptString method of the Receipt class
1 /**
2 * Prints the specified receipt. This dummy implementation
3 * prints to <code>System.out</code> instead of a printer.
4 *
5 * @param receipt
6 */
7 public void printReceipt(Receipt receipt) {
8 System.out.println(receipt.createReceiptString());
9 }
Listing 7.16 The outcome of the printReceipt method appears only in System.out.
143
Chapter 7 Testing
144
Chapter 7 Testing
47 Integer.toString(rentalTime.getYear())));
48 assertTrue("Wrong rental month.", result.contains(
49 Integer.toString(rentalTime.
50 getMonthValue())));
51 assertTrue("Wrong rental day.", result.contains(
52 Integer.toString(rentalTime.
53 getDayOfMonth())));
54 assertTrue("Wrong rental hour.", result.contains(
55 Integer.toString(rentalTime.getHour())));
56 assertTrue("Wrong rental minute.", result.contains(
57 Integer.toString(rentalTime.getMinute())));
58 }
59 }
Listing 7.17 The test for the createReceiptString method of the Receipt class
145
Chapter 7 Testing
1 /**
2 * Books the specified car. After calling this method, the car
3 * can not be booked by any other customer. This method also
4 * permanently saves information about the current rental.
5 *
6 * @param car The car that will be booked.
7 */
8 public void bookCar(CarDTO car) {
9 rental.setRentedCar(car);
10 rentalRegistry.saveRental(rental);
11 }
146
Chapter 7 Testing
32 new DrivingLicenseDTO("1234567"));
33 String regNo = "abc123";
34 CarDTO rentedCar = new CarDTO(regNo, new Amount(1000),
35 "medium", true,
36 true, "red");
37 instance.registerCustomer(rentingCustomer);
38 instance.bookCar(rentedCar);
39 List<Rental> savedRentals =
40 regCreator.getRentalRegistry().
41 findRentalByCustomerName(customerName);
42 int expectedNoOfStoredRentals = 1;
43 int noOfStoredRentals = savedRentals.size();
44 assertEquals("Wrong number of stored rentals.",
45 expectedNoOfStoredRentals,
46 noOfStoredRentals);
47 Rental savedRental = savedRentals.get(0);
48 Amount paidAmt = new Amount(5000);
49 CashPayment payment = new CashPayment(paidAmt);
50 savedRental.pay(payment);
51 savedRental.printReceipt(new Printer());
52 String result = outContent.toString();
53 assertTrue("Saved rental does not contain rented car",
54 result.contains(regNo));
55 }
56 }
Listing 7.19 The test for the bookcar method of the Controller class
The last class is Main, which has only the method main. This is very hard to test, since it
does nothing but create some objects. When the program is ready, it will most likely start a
user interface, then it will be possible to verify that something happens on the screen. While
this can not be done now, since no user interface is created, it is still possible to verify that
some chosen part of the output from the test run in View.sampleExecution appears on the
screen. It is also possible to inspect the JVM to see that the expected objects are created, but
this involves starting a debugger in another JVM, and attaching it to the inspected JVM, which
is too complicated for this course. Maybe even too complicated to be meaningful at all, if the
only purpose is to see that some new statements behave as expected.
That concludes the rent car testing case study. A total of 56 test methods were created,
which should be acceptable for such a small program, completely lacking exception handling.
All 56 tests pass, which gives the joyful sight presented in figure 7.12. Quite amazingly, the
SUT consists of 1353 lines of code in total, and the tests of 1322 (no cheating). A difference
of only two percent!
147
Chapter 7 Testing
Too few tests Both the most common and most severe mistake is probably not to write
enough tests. Try to cover all possible branches of if statements and loops. Also
NO!
try to write tests for extreme and illegal parameter values.
Too many assertions in the same test method Place as few assertions as possible in each
test method. It is clearer what happens and easier to give an explaining name to a test
method if it has few assertions. Ideally, there should only be one assertion per test
method, but it is not always possible to evaluate the outcome of a call to the SUT in
one single assertion. Sometimes more than one are actually required.
Not self-evaluating Test result should be evaluated using assertions, not with if statements
in the test methods, nor by forcing the tester to read output.
NO!
Producing output A test shall not write to System.out. The more tests there are, the
more confusing it becomes if they print some kind of status messages.
Worsen SUT design The design of the SUT shall not be worsened just to facilitate testing.
It is practically always possible to test without changing the SUT, even though it often
requires extra work.
148
Chapter 8
Handling Failure
A program is not complete if it does not
handle all possible failures. Some failures
have cures, and can be dealt with carefully
in order to make the program work despite
the exceptional condition that caused the
failure. In other cases, there is not much
more to do, than to report that the opera-
tion failed. In any case, the outcome of a
system operation shall never be undefined,
no matter what happens.
Many programming languages enables
using exceptions for error handling, which
is an important mechanism to make the
code more flexible and easier to under-
stand. An exception represents an ab-
normal situation, that disrupts the sequen-
tial execution path through the program.
Say, for example, that a method requires Figure 8.1 A program must handle abnormal situations,
giving the user appropriate feedback. Image by FreeIm-
a connection to a server to fulfill its task, ages.com/Alexandre Galant
and that this connection can not be estab-
lished. This means the method can not do what it is supposed to, and has to return immedi-
ately, informing the caller that the abnormal situation could not connect to server occurred.
The caller then has to switch to error handling, since it did not get any result from the called
method. This scenario is quite easily implemented using exceptions. Without them, it would
instead require messy if statements and return values, in order to check for possible error
codes.
8.1 UML
It is remarkably unclear and difficult to illustrate exception handling in UML. Figure 8.2 shows
two possible ways to draw exception handling in a class diagram. The curly brackets in figure
8.2a define a constraint, which means some condition or restriction related to the element
where it is placed. The content of a constraint is free text, anything can be written there.
149
Chapter 8 Handling Failure
This particular constraint specifies exceptions that the method may throw. Figure 8.2b, shows
another way to illustrate exceptions in a class diagram, using a reference to the exception class.
It has the disadvantage of not showing which method throws the exception.
(a)
(b)
Figure 8.3 illustrates how a sequence and a communication diagram can indicate that an
exception is thrown, interrupting the normal, sequential, flow. An open arrow is used, see
figure 8.3a, which means the message is asynchronous, and does not follow the sequential
flow. The stereotype «exception» is used to further clarify that the asynchronous message is
an exception. A stereotype says that an element belongs to a certain category of such elements.
Here, it says that the message belongs to the category thrown exception. Unfortunately, astah
can not draw an asynchronous message in a communication diagram. Therefore, a normal,
closed, arrow symbolizing a sequential message is used in the red circle in figure 8.3b. Note
that, at the blue arrow in the sequence diagram, there is an extra horizontal line in the activation
bar. This is because there is a new block starting here, to handle the message symbolizing the
exception. This block is not shifted to the right as usual, since the message is asynchronous.
It can be considered to represent the catch block. Finally, note the unfortunate fact that there
is a parenthesis, (), after the exception name in both diagrams.
(b)
(a)
150
Chapter 8 Handling Failure
1 /**
2 * Books the specified car. After calling this method, the car
3 * can not be booked by any other customer. This method also
4 * permanently saves information about the current rental.
5 *
6 * @param car The car that will be booked.
7 */
8 public void bookCar(CarDTO car) {
9 rental.setRentedCar(car);
10 rentalRegistry.saveRental(rental);
11 }
1 /**
2 * Specifies the car that was rented.
3 *
4 * @param rentedCar The car that was rented.
5 */
6 public void setRentedCar(CarDTO rentedCar) {
7 this.rentedCar = rentedCar;
8 carRegistry.setBookedStateOfCar(rentedCar, true);
9 }
1 /**
2 * If there is an existing car with the registration number
3 * of the specified car, set its booked property to the
151
Chapter 8 Handling Failure
152
Chapter 8 Handling Failure
Listing 8.4 A class that forces clients to handle exceptions even if nothing
is wrong.
Listing 8.5 A client of the class in listing 8.4 is forced to handle excep-
tions in a successful execution.
153
Chapter 8 Handling Failure
3 System.out.println(iter.next());
4 }
154
Chapter 8 Handling Failure
It is now time to decide class names for the exceptions used in the book car system
operation. First, consider the checked exception thrown when the car is already booked.
It could be called something like AlreadyBookedException, but that might be too de-
tailed. At the other end of the spectrum is RentalException, which could be used for
practically any exception in the entire car rental application. A possible compromise is
CarRegistryException, which could be used for all exceptions thrown by CarRegistry,
but not by any other class. When making this decision it is important to understand that what-
ever arrives at the user interface, must inform the cashier that the car was already booked.
Otherwise, the cashier will have to tell the customer Sorry, I can not book your car and I have
no clue why. This information can only have two forms, an error code contained in the ex-
ception object, or the name of the exception class. Each form has its drawbacks. Using error
codes brings the risk of messy if statements to decide what went wrong, using class names
brings the risk of both increasing the car registry’s public interface and requiring many catch
statements to decide what went wrong. It is not obvious which option to choose, but let’s
settle for the class name. Partly because it is not very object-oriented to use if statements to
check the primitive value of the error code, and partly because, at least this far, we have not
introduced a lot of different exception classes. Later, if too many exception classes appear, we
might have to change to another solution. The outcome of this discussion is thus to call the
class AlreadyBookedException. Next, consider the case that something goes wrong in the
underlying datastore. Here, CarRegistryException is a good name. There is no detailed
information to convey, except perhaps an error code from the database API, which anyway is
of no use in the view. We are now ready to create the exception classes, see listings 8.8 and
8.9.
1 /**
2 * Thrown when trying to book a car which is already booked.
3 */
4 public class AlreadyBookedException extends Exception {
5 }
1 /**
2 * Thrown when something goes wrong while performing an
3 * operation in the <code>CarRegistry</code>. The message
4 * might contain more information about the error condition.
5 */
6 public class CarRegistryException extends RuntimeException {
7 }
155
Chapter 8 Handling Failure
1 /**
2 * Thrown when trying to book a car which is already booked.
3 */
4 public class AlreadyBookedException extends Exception {
156
Chapter 8 Handling Failure
1 /**
2 * Thrown when something goes wrong while performing an
3 * operation in the <code>CarRegistry</code>. The message
4 * might contain more information about the error condition.
5 */
6 public class CarRegistryException extends RuntimeException {
7 /**
8 * Creates a new instance representing the condition
9 * described in the specified message.
10 *
11 * @param msg A message that describes what went wrong.
12 */
13 public CarRegistryException(String msg) {
14 super(msg);
15 }
16 }
157
Chapter 8 Handling Failure
Listing 8.12 An exception that is too detailed for higher layers is caught, and a
more appropriate exception is thrown instead.
158
Chapter 8 Handling Failure
3 storage.createCustomer(customer);
4 } catch (OperationFailedException exc) {
5 exc.getCause(); // Returns the original exception.
6 }
7 }
159
Chapter 8 Handling Failure
1 /**
2 * Specifies the car that was rented. The specified car is
3 * also booked, thus becoming unavailable to other rentals.
4 *
5 * @param rentedCar The car that was rented.
6 */
7 public void rentCar(CarDTO rentedCar)
8 throws AlreadyBookedException {
9 bookCar(rentedCar);
10 this.rentedCar = rentedCar;
11 }
12
13 private void bookCar(CarDTO carToBook)
14 throws AlreadyBookedException {
15 CarDTO currentCarState =
16 carRegistry.getCarByRegNo(carToBook);
17 if (currentCarState.isBooked()) {
18 throw new AlreadyBookedException(currentCarState);
19 }
20 carRegistry.setBookedStateOfCar(carToBook, true);
21 }
Listing 8.15 The setRentedCar method in Rental does not catch the
CarRegistryException.
We are not done yet. The CarRegistryException is now in bookCar in the controller,
and there the same question again arises. Shall the exception be caught, or again allowed to
propagate upwards, this time to the view? Now, the answer is a definite “no”. It is never a
good design to let the view depend on the integration layer. Therefore, the exception is caught
in the controller. Still, the view must be informed of the fact that the car could not be booked.
That means a new exception must be thrown by the Controller method bookCar. This
exception can be very generic, in order to be appropriate whenever any operation fails, but
the exact reason is not of interest to the view. Let’s use the OperationFailedException,
already introduced in listing 8.14. The Controller method bookCar is shown in listing
8.16.
1 /**
2 * Books the specified car. After calling this method, the car
3 * can not be booked by any other customer. This method also
4 * permanently saves information about the current rental.
5 *
6 * @param car The car that will be booked.
7 */
8 public void bookCar(CarDTO car) throws AlreadyBookedException,
9 OperationFailedException {
10 try {
160
Chapter 8 Handling Failure
11 rental.rentCar(car);
12 rentalRegistry.saveRental(rental);
13 } catch(CarRegistryException carRegExc) {
14 throw new OperationFailedException(
15 "Could not rent the car.",
16 carRegExc);
17 }
18 }
Listing 8.16 The bookCar method in Controller, when exception handling has been added.
This long discussion only treated CarRegistryException. We have not yet started to
consider AlreadyBookedException, but luckily, that is quite easy. Since the very reason
it was created was to convey information to the view, it most certainly shall propagate all the
way up to the view. As can be seen in listing 8.16, it is not caught in the controller, but is
instead specified in the throws clause, on line nine.
1 /**
2 * If there is an existing car with the registration number of
3 * the specified car, set its booked property to the specified
4 * value. Nothing is changed if the car’s booked property
161
Chapter 8 Handling Failure
Listing 8.17 The Javadoc comments for the methods setBookedStateOfCar and
getCarByRegNo.
Continuing upwards through the layers, the next class is Rental, and the method is
rentCar. This method does not catch a CarRegistryException coming from the car
registry. Therefore, the same exception is thrown also by this method, and must therefore be
documented. Also AlreadyBookedException is included in the Javadoc comment (listing
8.18), since it is thrown by this method.
1 /**
2 * Specifies the car that was rented. The specified car is
3 * also booked, thus becoming unavailable to other rentals.
4 *
5 * @param rentedCar The car that was rented.
6 * @throws AlreadyBookedException if the car was already
7 booked.
8 * @throws CarRegistryException if the database call failed.
9 */
10 public void rentCar(CarDTO rentedCar)
162
Chapter 8 Handling Failure
11 throws AlreadyBookedException {
12 ...
13 }
1 /**
2 * Books the specified car. After calling this method, the
3 * car can not be booked by any other customer. This method
4 * also permanently saves information about the current
5 * rental.
6 *
7 * @param car The car that will be booked.
8 * @throws AlreadyBookedException if the car was already
9 * booked.
10 * @throws OperationFailedException if unable to rent the car
11 * for any other reason than
12 * it being already booked.
13 */
14 public void bookCar(CarDTO car) throws AlreadyBookedException,
15 OperationFailedException {
16 ...
17 }
163
Chapter 8 Handling Failure
Make the object immutable An immutable object is an object that can never
change state. It is a programmers best friend, since there is never any doubt
about its state, and no risk that it is accidentally changed. To make an ob-
ject immutable, all of its fields must be final. Also, to be really rigid, it
must not be possible to inherit the class, and change its immutable behavior.
Listing 8.20 contains an immutable example, Person. Note that the object
address can not just be stored on line 15 in the constructor. It is necessary
to make a copy of it, otherwise also the object that created the Person would
have a reference to the same Address object, and be able to change it.
1 /**
2 * Objects of this class are immutable, none of
3 * the fields can ever change state.
4 */
5 public final class Person {
6 private final String name;
7 private final Address address;
8
9 /**
10 * Creates an instance with the specified
11 * name and address.
12 */
13 public Person(String name, Address address) {
14 this.name = name;
15 this.address = new Address(address);
16 }
17 }
18
19 /**
20 * Represents an address.
21 */
22 public class Address {
23 private String street;
24 // More fields.
25
26 /**
27 * Creates an instance with all fields equal
28 * to the fields of the specified address.
29 */
30 Address(Address address) {
31 this.street = address.street;
32 // Copy all other fields.
33 }
34 }
Listing 8.20 Objects of Person are immutable, their fields can never
change value.
164
Chapter 8 Handling Failure
Check parameter values before changing any state A common cause of an ex-
ception is an illegal parameter value. Therefore, it is a very good practice to
let each method check for illegal parameter values first, before performing
its work. Consider for example the withdraw method in listing 8.21, which
throws OverdraftException when the amount to withdraw is larger than
the balance. Since the first thing it does is to check the parameter value (line
15), there is no risk that the state, which in this case is the balance, is changed
when the exception is thrown.
1 /**
2 * Represents a bank account.
3 */
4 public class Account {
5 private int balance;
6
7 /**
8 * Withdraws the specified amount from this
9 * account.
10 *
11 * @param amount The amount to withdraw.
12 */
13 public void withdraw(int amount) throws
14 OverdraftException {
15 if (amount > balance) {
16 throw new OverdraftException(balance,
17 amount);
18 }
19 balance = balance - amount;
20 }
21 }
Listing 8.21 This object checks if the amount to withdraw is illegal, before
it updates the balance.
Place operations that might fail before operations that alter the state This strat-
egy can be used if it is not possible to validate the parameters without per-
forming a part of the methods work. Consider, for example, a method that
shall insert a new element in a sorted collection of unique elements. The
best way to check if the new element is unique is to find the location where
it shall be inserted. This search will fail if the element is not unique, but the
collection will not be updated at that point.
Use a temporary copy of the state If none of the above strategies can be used,
we can save a temporary copy of the state and make sure to restore it before
throwing an exception.
165
Chapter 8 Handling Failure
Now let’s check if all exception throwing methods in the case study follow this prac-
tice. Starting in CarRegistry, the methods to consider are getCarByRegNo and
setBookedStateOfCar. The first is not of interest, since it does not change any state at
all. The second must check if the car exists before changing its booked state. This is an exam-
ple where the strategy Place operations that might fail before operations that alter the state
is appropriate. To be able to change the state of the car, we must find it in the datastore. If
it is not found, the state can not be changed, but instead an exception is thrown. Continuing
to Rental, the method rentCar uses the strategy Check parameter values before changing
any state. The first thing the method does is to check that the specified car exists, the second
is to check its booked status. Only if the car exists and is not yet booked, is the state updated.
If it does not exist, or is already booked, an exception is thrown. Finally, in Controller,
bookCar does never change any state.
1 try {
2 CarDTO availableCar = new CarDTO(null, new Amount(1000),
166
Chapter 8 Handling Failure
167
Chapter 8 Handling Failure
similar error handling code over many catch blocks, but instead just call the component
responsible for error messages, and encapsulate user interface handling in there.
The case study does not have a real user interface, but we can still create a class that
prints error messages to System.out, see listing 8.23. It is called from the catch blocks,
as in listing 8.24. Since there was no need for a unique error message in answer to an
OperationFailedException, it is no longer caught explicitly, but is handled by the catch
block for any java.lang.Exception. Note that the error message is created in the view, it
is not the string returned by getMessage in Exception, since that would have meant some
lower layer decided what to print in the user interface.
1 /**
2 * This class is responsible for showing error messages to
3 * the user.
4 */
5 public class ErrorMessageHandler {
6
7 /**
8 * Displays the specified error message.
9 *
10 * @param msg The error message.
11 */
12 void showErrorMsg(String msg) {
13 StringBuilder errorMsgBuilder = new StringBuilder();
14 errorMsgBuilder.append(createTime());
15 errorMsgBuilder.append(", ERROR: ");
16 errorMsgBuilder.append(msg);
17 System.out.println(errorMsgBuilder);
18 }
19
20 private String createTime() {
21 LocalDateTime now = LocalDateTime.now();
22 DateTimeFormatter formatter = DateTimeFormatter.
23 ofLocalizedDateTime(FormatStyle.MEDIUM);
24 return now.format(formatter);
25 }
26 }
Listing 8.23 The class ErrorMessageHandler, which is responsible for showing error mes-
sages to the user.
168
Chapter 8 Handling Failure
1 /**
2 * This class is responsible for the log.
3 */
4 public class LogHandler {
5 private static final String LOG_FILE_NAME =
6 "rentcar-log.txt";
7 private PrintWriter logFile;
8
9 public LogHandler() throws IOException {
10 logFile = new PrintWriter(
11 new FileWriter(LOG_FILE_NAME), true);
12 }
13
14 /**
15 * Writes log entries.
16 *
17 * @param entry The log entry.
18 */
169
Chapter 8 Handling Failure
170
Chapter 8 Handling Failure
1 @Test
2 public void testSetRentedCarWhenCarIsBooked() {
3 CarRegistry carReg = new RegistryCreator().
4 getCarRegistry();
5 Rental instance = new Rental(null, carReg);
6 CarDTO rentedCar = new CarDTO("abc123", new Amount(1000),
7 "medium", true,
8 true, "red", false);
9 try {
10 carReg.setBookedStateOfCar(rentedCar, true);
11 instance.rentCar(rentedCar);
12 fail("Could rent a booked car.");
13 } catch (AlreadyBookedException ex) {
14 assertTrue("Wrong exception message, does not " +
15 "contain specified car: " +ex.getMessage(),
16 ex.getMessage().contains(
17 rentedCar.getRegNo()));
18 assertTrue("Wrong car is specified: " +
19 ex.getCarThatCanNotBeBooked(),
20 ex.getCarThatCanNotBeBooked().getRegNo().
21 equals(rentedCar.getRegNo()));
22 }
23 }
24
25 @Test
26 public void testSetRentedCarWhenCarDoesNotExist() throws
27 AlreadyBookedException {
28 CarRegistry carReg =
29 new RegistryCreator().getCarRegistry();
30 Rental instance = new Rental(null, carReg);
31 CarDTO rentedCar = new CarDTO("wrong", new Amount(1000),
171
Chapter 8 Handling Failure
32 "medium", true,
33 true, "red", false);
34 try {
35 instance.rentCar(rentedCar);
36 fail("Could rent a non-existing car.");
37 } catch (CarRegistryException exc) {
38 assertTrue("Wrong exception message, does not " +
39 " contain specified car: "
40 + exc.getMessage(),
41 exc.getMessage().contains(
42 rentedCar.toString()));
43 }
44 }
172
Chapter 8 Handling Failure
Failure handling is missing The fact that the program works does not necessarily mean that
failure handling is correct. It is a common mistake not handle all possible failures, for
example not to check that all parameters in a method have valid values. A good way
to discover this flaw is to write extensive unit tests. Unit tests shall check all possible
parameter values, and all possible execution paths, which means erroneous conditions
will be discovered.
NO!
Exceptions are caught when it is not needed Do not catch an exception if there is nothing
to do in the catch block. It is quite common to misunderstand the practice saying
Use the correct abstraction level for exceptions, and always catch all exceptions in
all methods. The result is code similar to listing 8.29, where, on lines four to six,
the exception is caught just to be rethrown. If the exception is appropriate also in the
calling method, then it is best to just let it propagate further up in the call stack, as in
listing 8.30.
1 public void myMethod() throws MyException {
2 try {
3 methodThatThrowsMyException();
4 catch(MyException exc) {
5 throw exc;
6 }
7 }
Listing 8.29 There is no point in catching an exception just to immediately rethrow it, as in this
listing.
Listing 8.30 If the exception makes sense also in the calling method, it can continue to that method.
173
Chapter 9
9.1 UML
In order to design polymorphism and inheritance, it is nec-
essary to model interfaces, implementations and inheri-
tances in UML. How do this in a class diagram is illustrated
in figure 9.1, where ClassA implements InterfaceA,
and ClassB inherits ClassC. There are a few sub-
tle things worth emphasizing here. First, the stereotype
«interface» is used to tell that InterfaceA in an in-
terface and not a class. A stereotype is a categorization.
Here, it says that InterfaceA belongs to the category in-
(a) terfaces. This stereotype is the only difference between the
symbols for class and interface. Another issue is that the
word implementation does not exist in UML, instead, the
same thing is called realization. Also, the word inheritance
is very vaguely specified and not much used. Instead, a su-
perclass is said to be a generalization of a subclass. Fur-
thermore, note that the method methodA in the interface is
written in italics. This means it is abstract, it consists only
(b)
of a declaration and does not have any body.
Figure 9.1 Class diagram, illustrating: It is an unfortunate fact that there is now way to il-
(a) interface and implementation lustrate implementation or inheritance in a sequence or
(b) inheritance
174
Chapter 9 Polymorphism and Inheritance
(a) (b)
(c)
Figure 9.2 An object of a class that implements an interface can be illustrated as the class, figure (a), or as the
interface, figure (b), but not as both, figure (c).
9.2 Polymorphism
Polymorphism is Greek for many forms, the result of
Figure 9.3 Using icon notation to illustrate
an interface. using polymorphism is that one single public interface
can have any number of different implementations.
How to achieve this will be explained with the help
of a concrete example, namely a logging API. For a start, this API consists of one single class,
which can write log entries to a file, listing 9.1. The public interface of this class is colored
blue in the listing. It consists of the class name, the definition of the constructor, and the
definition of the log method. Which part of this public interface is really necessary to know
175
Chapter 9 Polymorphism and Inheritance
1 package se.leiflindback.oodbook.polymorphism.logapi;
2
3 import java.io.FileWriter;
4 import java.io.IOException;
5 import java.io.PrintWriter;
6
7 /**
8 * Prints log messages to a file. The log file will be in the
9 * current directory and will be called log.txt.
10 */
11 public class FileLogger {
12 private PrintWriter logStream;
13
14 /**
15 * Creates a new instance and also creates a new log file.
16 * An existing log file will be deleted.
17 */
18 public FileLogger() {
19 try {
20 logStream = new PrintWriter(
21 new FileWriter("log.txt"), true);
176
Chapter 9 Polymorphism and Inheritance
Listing 9.1 In the first version, the logging API contains only this class. The public interface is
colored blue.
1 package se.leiflindback.oodbook.polymorphism.logapi;
2
3 /**
4 * A client for the logger. Prints log messages to the
5 * specified logger.
6 */
7 public class AnyClassThatNeedsToLogSomething {
8 private FileLogger logger;
9
10 public void setLogger(FileLogger logger) {
11 this.logger = logger;
12 }
13
14 /**
15 * Prints to the log. The logged string includes the
16 * specified message number.
17 *
18 * @param msgNo This number is included in the logged
19 * string.
20 */
21 public void anyMethod(int msgNo) {
22 logger.log("Important message number " + msgNo);
23 }
24 }
177
Chapter 9 Polymorphism and Inheritance
1 package se.leiflindback.oodbook.polymorphism.logapi;
2
3 /**
4 * Specifies an object that can print to a log. This interface
5 * does not handle log locations, it is up to the implementing
6 * class to decide where the log is.
7 */
8 public interface Logger {
9
10 /**
11 * The specified message is printed to the log.
12 *
13 * @param message The message that will be logged.
14 */
15 void log(String message);
16 }
1 package se.leiflindback.oodbook.polymorphism.logapi;
2
3 /**
4 * A client for the logger. Prints log messages to the
5 * specified logger.
6 */
7 public class AnyClassThatNeedsToLogSomething {
8 private Logger logger;
9
10 public void setLogger(Logger logger) {
11 this.logger = logger;
12 }
13
14 /**
15 * Prints to the log. The logged string includes the
16 * specified message number.
17 *
18 * @param msgNo This number is included in the logged
19 * string.
20 */
21 public void anyMethod(int msgNo) {
22 logger.log("Important message number " + msgNo);
23 }
24 }
Listing 9.4 A client of the log API, when FileLogger implements the Logger interface
in listing 9.3.
178
Chapter 9 Polymorphism and Inheritance
This code is quite amazing already now. It contains an API that has encapsulated the name
of the class doing the work of the API! This is extremely low coupling, the client is using
a class, FileLogger, without any dependency on the name of that class. Still, we are far
from done, it is about to become much more amazing. Now is the time to actually make
use of polymorphism, that is, to provide more than one implementation of a single public
interface. The public interface for the logging API consists of the log method in the logger
interface. This public interface currently has one implementation, provided by FileLogger.
The second implementation will be a class, ConsoleLogger in listing 9.5, which prints the
log messages to the screen, instead of to a file. Also this implementation fulfills the contract
of the public interface defined by log, which, according to the JavaDoc, is that The specified
message is printed to the log.
1 package se.leiflindback.oodbook.polymorphism.logapi;
2
3 /**
4 * Prints log messages to <code>System.out</code>.
5 */
6 public class ConsoleLogger implements Logger {
7
8 /**
9 * Prints the specified string to <code>System.out</code>.
10 *
11 * @param message The string that will be printed to
12 * <code>System.out</code>.
13 */
14 @Override
15 public void log(String message) {
16 System.out.println(message);
17 }
18 }
Listing 9.5 The second implementation of the Logger interface is this class, ConsoleLogger,
which prints the log messages to the screen.
179
Chapter 9 Polymorphism and Inheritance
Figure 9.5 contains a class diagram with all classes created so far. The client of the log API,
AnyClassThatNeedsToLogSomething depends only on the interface Logger. It has no
coupling whatsoever to the implementations of the interface! This fact is the foundation of all
fancy usages of polymorphism. It means that AnyClassThatNeedsToLogSomething does
not know, and has no interest in, which class it is actually calling. All that matters is that it is a
class providing an implementation of the required public interface. This is guaranteed by the
compiler, since any object passed to setLogger must be of a class implementing Logger,
and must therefore provide an implementation of the public interface defined in Logger. It is
now possible to change implementation at runtime, as is done when using the main method
of the class Main, in listing 9.6. Make sure you fully understand this example, similar designs
are extremely common in object-oriented programming.
1 package se.leiflindback.oodbook.polymorphism.logapi;
2
3 /**
4 * Contains the main method of the log API client.
5 */
6 public class Main {
7
8 /**
9 * @param args The program does not take any command line
10 * parameters.
11 */
12 public static void main(String[] args) {
13 AnyClassThatNeedsToLogSomething client
14 = new AnyClassThatNeedsToLogSomething();
15
16 client.setLogger(new FileLogger());
17 client.anyMethod(1);
18 client.anyMethod(2);
19 client.anyMethod(3);
20
21 client.setLogger(new ConsoleLogger());
22 client.anyMethod(4);
23 client.anyMethod(5);
24 client.anyMethod(6);
25 }
26 }
Listing 9.6 A main method of a program using the log API. The first three logged messages are
printed to the file, the last three to the screen.
In conclusion, by using polymorphism the program gets higher cohesion, since the client
of the polymorphic design, AnyClassThatNeedsToLogSomething in the example above,
does not contain any code related to choosing implementation of the public interface. It just
uses the implementation passed to it (in the method setLogger). There will also be lower
180
Chapter 9 Polymorphism and Inheritance
coupling, since there is no coupling from the client to any implementation. Finally, the pro-
gram gets better encapsulation, since the names of the implementing classes are completely
encapsulated inside the logging API.
The result of improving both coupling, cohesion and encapsulation, is that the behavior of
the program can be changed at runtime, by switching implementation of a public interface.
This was done in the above example by changing from FileLogger to ConsoleLogger.
Without polymorphism, the destination of the logs would have been hard-coded in the pro-
gram. The only way to change logging at runtime would have been with if statements, like
the code in listing 9.7. Such if statements would have had to be repeated in every class that
wanted to log something. Also, they would have to be updated whenever a new implementa-
tion was added.
1 if (logToFile) {
2 Write to the file
3 } else if(logToScreen) {
4 Write to the screen
5 } similar statements checking other log destinations
1 package se.leiflindback.oodbook.polymorphism.logapi;
2
3 /**
4 * Contains a main method of the log API client, which loads
5 * new <code>Logger</code> implementations at runtime.
6 */
7 public class LoadImplAtRuntime {
8 private int msgNo = 1;
9 private AnyClassThatNeedsToLogSomething client =
10 new AnyClassThatNeedsToLogSomething();
11
12 /**
13 * @param args Each command line parameter shall be the
181
Chapter 9 Polymorphism and Inheritance
Listing 9.8 A main method that can load classes at runtime. To make this class load and
use FileLogger (listing 9.1) and ConsoleLogger (listing 9.5), the command line pa-
rameters shall be se.leiflindback.oodbook.polymorphism.logapi.FileLogger
se.leiflindback.oodbook.polymorphism.logapi.ConsoleLogger
9.3 Inheritance
Inheritance is a very special kind of relation between classes. If one class, the subclass, inherits
another class, the superclass, it means that all non-private members of the superclass also
become members of the subclass. In fact, all code in the superclass, except code with private
visibility, becomes code also in the subclass. This very special kind of relation has severe
consequences, as will be explained below. Before proceeding, it might adequate to repeat the
basics of inheritance, by reading section 1.9.
Another thing that must be understood is protected visibility. This is the fourth type of
visibility, besides public, private and package private. A member with protected visibility can
be accessed by a subclass in any package, but by a non-subclass only in the same package. This
is illustrated in figure 9.6. Note that the symbol for protected visibility is the hash character,
#. Since the protected member is visible to classes in any package, it is part of the public
interface, and not of the implementation. It is thus more closely related to public visibility
than to private or package private.
182
Chapter 9 Polymorphism and Inheritance
(a) (b)
(c) (d)
Figure 9.6 The method protectedMethod, which has protected visibility, can be called by a method in
Class2 in all situations except diagram (d).
Much is written on creating classical inheritance hierarchies, like the one in figure 9.7.
However, they are often not as useful as it might seem, but instead make it difficult to reuse
code. This class hierarchy describes animals, now let’s focus on how they move. The diagram
183
Chapter 9 Polymorphism and Inheritance
shows that birds fly, fishes swim and mammals walk. Seems OK, but what if want to add a
penguin, which is a bird that swims but does not fly? Or a mammal that can both swim and
walk? And what about a creature that changes behavior as it grows, for example a bird that can
not fly when it is hatched, but later learns to? This reasoning might suggest it would be better
to create a class hierarchy based on movement possibilities than on taxonomy, as in figure 9.8.
Figure 9.8 The animal inheritance hierarchy, based on how species move, instead of taxonomy.
Unfortunately, this change does not solve the problem. First, we would need multiple in-
heritance for the mammal that both swims and walks. Second, it would still be impossible to
change movement for the bird that learns to fly. Third, there are many properties of animals
which do not fit in the movement hierarchy. Consider for example wings, ostriches have wings
but can not fly, so the wing property can not be placed in Flyer. Above all, why create any
hierarchy at all? Why not use composition instead, as in figure 9.9? This way, any animal
object can be connected to any movement object, or to several movement objects. Also, an
animal object can, at any point in time, change the set of available movement objects. The
solution should be improved further, using polymorphism for the movement, and maybe the
strategy pattern described below. However, what is important here is only the advantage of
composition over inheritance. To be honest, though, it must be admitted that the diagram in
figure 9.9 is messier than those in figures 9.7 and 9.8. This is the disadvantage of composition,
that it becomes more difficult to understand which class has a reference to which, especially
when references change at runtime, and even more so if polymorphism is used.
Inheritance makes the code difficult to maintain, and may introduce bugs, since it breaks
encapsulation. Not only the public interface of the superclass is inherited by the subclass, but
also the implementation. Simply speaking, everything in the superclass becomes a part also
of the subclass. It should be obvious that the mere fact of completely breaking encapsulation
is something bad. Now, let’s look at an example illustrating the risk of inheriting a class that
was not written to be inherited, just for the purpose of code reuse.
Suppose we have at our disposal an API, that includes a class List, part of which is shown
in listing 9.9. Unfortunately, this class does not really meet our needs, since we want a list
184
Chapter 9 Polymorphism and Inheritance
Figure 9.9 Using composition, instead of inheritance, for the animal classes.
that counts how many items have been added during the entire existence of a List object (not
how many items there are currently in the list). We therefore decide to create a new class,
CountingList, which inherits List and adds the missing functionality. This can be done as
in listing 9.10.
185
Chapter 9 Polymorphism and Inheritance
Listing 9.10 An attempt to extend the list from listing 9.9, and count how many items have ever
been added to the list.
This seems to be a good solution, but it does not work. When addAll is called, the amount
of added elements is doubled. Adding five elements increases the noOfAddedElems counter
by ten. The reason is the addAll in List was implemented as in listing 9.11. This method
iterates over the list and calls add for each element, in order not to duplicate the code that
adds a single element. However, that call is now to add in CountingList, and thereby the
counter is incremented twice, both in addAll and add.
There are of course different ways to get rid of this bug, for example not to increment
noOfAddedElems in addAll. However, such solutions only make CountingList ever
more dependent on particularities of List’s implementation. This implementation is of course
not documented, since it is supposed to be encapsulated. Also, it might very well be that the
List implementation changes between releases, without any notice, which might introduce
new bugs in CountingList. The conclusion is that inheritance is not appropriate here. It
is much better to reuse the existing list class with composition, as in listing 9.12. Just as
in the animal example above, composition makes the code a bit longer. In this case since
CountingList must include all List methods that shall be available. It is, in fact, not un-
common that composition is less elegant than inheritance seems to be, at first sight. However,
composition has the big advantage that it actually works, and it is much less likely to create
problems in the future, when the code is changed.
186
Chapter 9 Polymorphism and Inheritance
1 /**
2 * A list that counts how many elements have ever been added.
3 */
4 public class CountingListUsingComposition {
5 private int noOfAddedElems;
6 private List list = new List();
7
8 public void add(Object elemToAdd) {
9 noOfAddedElems++;
10 list.add(elemToAdd);
11 }
12
13 public void addAll(
14 CountingListUsingComposition elemsToAdd) {
15 noOfAddedElems = noOfAddedElems + elemsToAdd.size();
16 list.addAll(elemsToAdd.list);
17 }
18
19 /**
20 * Tells how many elements have ever been added to
21 * the list.
22 *
23 * @return the number of elements that have ever been
24 * added to the list.
25 */
26 public int noOfAddedElems() {
27 return noOfAddedElems;
28 }
29
30 public int size() {
31 return list.size();
32 }
33
34 public Object get(int index) {
35 return list.get(index);
36 }
37 }
Third Reason to Prefer Composition: There is no Way to Control the Public Interface of
the Subclass
A subclass blindly accepts the entire public interface of its superclass. If those classes have
different authors, the subclass’ author has effectively given up control of the created class’
187
Chapter 9 Polymorphism and Inheritance
public interface. Not a very nice situation. This problem arises when using inheritance for
code reuse, thus inheriting a class whose author does not know it has been inherited. This
is the situation with the code in listing 9.10, where CountingList has the entire public
interface of List. In that case, this might seem appropriate, but can we really be sure we want
everything from List to appear also in CountingList? As an example, java.util.List
has 35 methods (in JDK 8). If the list class inherited here is of a similar size, it is quite an
effort just to go through all those methods and decide if they suit also the subclass. Then,
it is necessary to keep track of when new versions of the superclass are released, and check
those for changes to the public interface. If a new superclass version is not checked, the public
interface of the subclass might change without anyone changing its code, and without anyone
knowing it changed.
It is sometimes the case that the task of one class is very similar to the task of another class,
except in some detail. Consider for example a class that creates a bar chart illustrating a data
set, which is read from a database. Such a class could be written as in listing 9.13
Listing 9.13 Pseudocode for a class that reads data from a database, and presents it in a bar chart.
188
Chapter 9 Polymorphism and Inheritance
Now suppose we also want to present the data as a line chart. This would require another
class, LineChart, which would be identical to BarChart except for the image creation on
line eleven. Obviously there is duplicated code, but how to remove it? the problem is that
the duplicated code forms the structure of the class, the handling of dataId, the try-catch
block, etc. A solution is to create a superclass that contains all common code, and place the
specific code in a method that can be overridden in subclasses, as in listing 9.14.
This way of placing implementation specific code in a protected method is in fact a design
pattern called template method, which is explained in more detail below, in section 9.4.
189
Chapter 9 Polymorphism and Inheritance
Listing 9.14 Pseudocode for classes presenting data as bar chart and line chart, without dupli-
cated code.
In spite of the critique of classical inheritance hierarchies above, they can still be useful. The
point is they should not be used blindly, but only under the right circumstances. First of all the
following conditions must be met.
190
Chapter 9 Polymorphism and Inheritance
Unfortunately, the above conditions are only necessary, but not sufficient. In fact, the hi-
erarchy of animals above, which prohibited code reuse, does meet all four requirements. To
formulate a sufficient condition is notably more difficult. A rough rule of thumb could be
that inheritance hierarchies are not very useful for real world entities, such as the animals.
The reason is that such entities are complex, and do not always follow a strict generalization-
specialization tree structure. Instead, inheritance is more useful for purely fabricated entities,
that do not really exist in the real world.
An example of a well-functioning hierarchy, not modeling real world entities, is the collec-
tion api in java.util. A small subset of the classes in this api is illustrated in figure 9.10.
For example, there is the interface List, which defines the contract of a list. This interface
is implemented by AbstractList, which provides code common for both linked lists and
array lists. Then, the classes ArrayList and LinkedList contain code that is specific for a
particular kind of list. When creating these classes, the author is free to decide which classes
shall exist, how they relate to each other, and which functionality to put where. With the ani-
mals, there is no such freedom. A penguin is a bird and a salmon is a fish, there is no arguing
about that.
Figure 9.10 Part of the collection api in java.util, which is an example of a class hierarchy modeling
fabricated entities instead of real world entities.
191
Chapter 9 Polymorphism and Inheritance
192
Chapter 9 Polymorphism and Inheritance
method. The observed class does not care what else the observing object can do, or what its
purpose is, as long as it can receive notifications.
Case Study
Here, an observer will be used to solve a problem we have been facing since we started to
design, namely how to pass data from model to view, when that data can not be a return value
to a method call from the view. As a case study, the car rental program is augmented with a
display on the wall in the office, telling how many cars of each size the company has rented
out. Whenever a rental is payed, no matter where or by who, the display shall be updated with
the rented car.
1 package se.leiflindback.oodbook.despat.observer;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * The observed class in a general implementation of the
8 * observer pattern.
9 */
10 public class ObservedClass {
11 private List<Observer> observers = new ArrayList<>();
12
13 /**
14 * Registers observers. Any <code>Observer</code> that is
15 * passed to this method will be notified when this object
16 * changes state.
17 *
18 * @param observer The observer that shall be registered.
19 */
20 public void addObserver(Observer observer) {
21 observers.add(observer);
22 }
23
24 // Called by any method in this class that has changed
25 // the class’ state.
26 private void notifyObservers() {
27 for (Observer observer : observers) {
28 observer.stateHasChanged();
29 }
30 }
31 }
Listing 9.15 The observed class, note that there is no reference to the observing class, only to the
Observer interface.
193
Chapter 9 Polymorphism and Inheritance
There are many reasons why this is difficult to implement. It is not easy to handle the
update directly in the view, letting the user interface where the rental is payed update the
display showing rented out cars. First, there might be many different ways to rent a car, in
different locations, and they can not all know about the display. Second, even if there is only
one single place where a customer can rent a car, it is still bad design to let one object in the
view be responsible for updating other view objects. That knowledge, what to do in response
to a particular user action, is business logic, and should therefore not be handled by the view
itself. A view object shall only call the controller and update itself to reflect the result of
the call. Finally, in the current rent car implementation, the view does not keep track of the
rental being paid for, that is done by the controller. There is no reason to give the view the
responsibility to know the size of the rented car, that would reduce cohesion in the view, since
it is the responsibility of Rental, in the model. Also, there would certainly be some amount
of duplicated code if both Rental and view had to know which car was being rented.
Having established that the display can not be updated by view object(s) responsible for the
user interface where the car is rented, we face a brick wall. There is no other object in the
view, and it would ruin MVC if the controller or any lower layer called the display to update
the number of rented cars. The solution is to use an observer, which will allow the model to
call the view without any dependency on the view.
Solution
1 package se.leiflindback.oodbook.rentcarWithExAndDesPat.model;
2
3 /**
4 * A listener interface for receiving notifications about
5 * rented cars. The class that is interested in such
6 * notifications implements this interface, and the object
7 * created with that class is registered with
8 * <code>addRentalObserver</code>. When a car is rented, that
9 * object’s <code>newRental</code> method is invoked.
10 */
11 public interface RentalObserver {
12 /**
13 * Invoked when a rental has been paid.
14 *
15 * @param rentedCar The car that was rented.
16 */
17 void newRental(CarDTO rentedCar);
194
Chapter 9 Polymorphism and Inheritance
18 }
The next decision is which class to observe. The required notification is to tell which
type of car was rented when a payment is accepted. Therefore, the observed class must have
knowledge about both payment and rented car. These requirements are met by Rental, which
is also chosen. To make it observable, it must have a method that registers observers. This
method is called addObserver, just as in the general example in listing 9.15. Also, it is good
practice to add a private method, notifyObservers, which is called by any other method
when notifications shall be sent. When shall observers be notified? In this case, the only state
change of interest is that a payment is accepted, therefore, notifyObservers is called last
in the method pay. Listing 9.17 contains the updated parts of Rental.
1 package se.leiflindback.oodbook.rentcarWithExAndDesPat.model;
2
3 public class Rental {
4 private List<RentalObserver> rentalObservers =
5 new ArrayList<>();
6
7 public void pay(CashPayment payment) {
8 payment.calculateTotalCost(this);
9 this.payment = payment;
10 notifyObservers();
11 }
12
13 private void notifyObservers() {
14 for (RentalObserver obs : rentalObservers) {
15 obs.newRental(rentedCar);
16 }
17 }
18
19 public void addRentalObserver(RentalObserver obs) {
20 rentalObservers.add(obs);
21 }
22 }
Then it is time to create the observer implementation. It shall be a class in the view, sim-
ulating the display telling the number of rented cars. Currently, there is only one class in the
view, which contains the hardcoded sample execution. To improve cohesion, a new class,
RentedCarsDisplay, is created. This class implements RentalObserver, and simply
prints the required information to System.out, see listing 9.18. As can be seen on line 28, it
was appropriate to include a CarDTO representing the rented car as parameter in the notifica-
tion method, newRental, since CarDTO contains the required information.
195
Chapter 9 Polymorphism and Inheritance
Previously, the size of the rented car was represented as a String. That was never really
appropriate, due to the risk of misspelling. Now that the size is being used on many places in
the code, this problem is growing. Therefore, size is changed to be defined using an enumera-
tion. This enumeration is placed in CarDTO, see listing 9.19, to make it available everywhere
a CarDTO is used.
1 package se.leiflindback.oodbook.rentcarWithExAndDesPat.view;
2
3 /**
4 * Shows a running total of rented cars of each type.
5 */
6 class RentedCarsDisplay implements RentalObserver {
7 private Map<CarDTO.CarType, Integer> noOfRentedCars =
8 new HashMap<>();
9
10 /**
11 * Creates a new instance, with the all counters of rented
12 * cars set to zero.
13 */
14 public RentedCarsDisplay() {
15 for (CarDTO.CarType type : CarDTO.CarType.values()) {
16 noOfRentedCars.put(type, 0);
17 }
18 }
19
20 @Override
21 public void newRental(CarDTO rentedCar) {
22 addNewRental(rentedCar);
23 printCurrentState();
24 }
25
26 private void addNewRental(CarDTO rentedCar) {
27 int noOfRentedCarsOfThisType =
28 noOfRentedCars.get(rentedCar.getSize()) + 1;
29 noOfRentedCars.put(rentedCar.getSize(),
30 noOfRentedCarsOfThisType);
31 }
32
33 private void printCurrentState() {
34 System.out.println("### We have now rented out ###");
35 for (CarDTO.CarType type : CarDTO.CarType.values()) {
36 System.out.print(noOfRentedCars.get(type));
37 System.out.print(" ");
38 System.out.print(type.toString().toLowerCase());
39 System.out.println(" cars.");
40 }
196
Chapter 9 Polymorphism and Inheritance
41 System.out.println("##############################");
42 }
43 }
197
Chapter 9 Polymorphism and Inheritance
3 new ArrayList<>();
4
5 public void registerCustomer(CustomerDTO customer) {
6 // Unchanged code.
7 rental.addRentalObservers(rentalObservers);
8 }
9
10 /**
11 * The specified observer will be notified when a rental
12 * has been paid. There will be notifications only for
13 * rentals that are started after this method is called.
14 *
15 * @param obs The observer to notify.
16 */
17 public void addRentalObserver(RentalObserver obs) {
18 rentalObservers.add(obs);
19 }
20 }
Comments
There is much worth pondering about the observer pattern. Here are some issues concerning
the implementation.
• What shall the observer know about the observed class? The observer
knows nothing at all about the observed class in the case study above. In fact,
there is no way for it to get a reference to the object that sent the notification.
Instead, all relevant data is passed as a parameter in the call of the notification
method, in this case, the data is a CarDTO object. This solution becomes
more and more problematic as more data is required. Also, whenever the
need for data changes, the parameter list of the notification method must
change, which means the public interface of Observer is changed.
Another solution is to not pass any data at all, but instead a reference to the
object that changed state, the Rental object in the above case study. The ob-
server can then call get methods in the observed class to collect needed data.
This solution is more dynamic, since different observers can collect different
data, as required. Also, the parameter list of the notification method consists
of one single parameter, the object that changed state, and will never change.
The downside of such an implementation is that the observer now depends
on the observed class, thereby increasing coupling. To alleviate this prob-
lem, we can introduce yet one interface, call it Observed, implemented by
the observed class. This interface shall contain only methods of the observed
198
Chapter 9 Polymorphism and Inheritance
class allowed to call by the observer. Now, the observer will know only about
this interface, not about the actual class being observed.
199
Chapter 9 Polymorphism and Inheritance
Overview
Case Study
The logging API from section 9.2 would be a great case study. Still it is not used, since there
are already many existing logging APIs. It would be a bit inappropriate to spend a lot of effort
developing yet another. The case study used here is instead the search for an available car
matching the wishes of a customer. This is performed in CarRegistry, where the current
search algorithm, listing 9.22, considers a car to be a match if all properties except registration
number are equal to those of the searched car. Properties equal to null or zero are ignored,
and the matching car must not be booked. This is, however, only one of many possible ways
to match existing cars against a customer’s wishes.
1 /**
2 * Search for a car that is not booked, and that matches the
3 * specified search criteria.
4 *
5 * @param searchedCar This object contains the search
6 * criteria. Fields in the object that are
7 * set to <code>null</code> or zero are
8 * ignored.
9 * @return A description matching the searched car’s
10 * description if an unbooked car with the
11 * same features as <code>searchedCar</code> was
12 * found, <code>null</code> if no such car was found.
13 * /
14 public CarDTO findAvailableCar(CarDTO searchedCar) {
15 for (CarData car : cars) {
16 if (matches(car, searchedCar)) {
17 return new CarDTO(car.regNo,
18 new Amount(car.price), car.size,
19 car.AC, car.fourWD, car.color,
20 false);
21 }
22 }
200
Chapter 9 Polymorphism and Inheritance
23 return null;
24 }
25
26 private boolean matches(CarData found, CarDTO searched) {
27 if (searched.getPrice() != null &&
28 !searched.getPrice().equals(new Amount(found.price))){
29 return false;
30 }
31 if (searched.getSize() != null &&
32 !searched.getSize().equals(found.size)) {
33 return false;
34 }
35 if (searched.getColor() != null &&
36 !searched.getColor().equals(found.color)) {
37 return false;
38 }
39 if (searched.isAC() != found.AC) {
40 return false;
41 }
42 if (searched.isFourWD() != found.fourWD) {
43 return false;
44 }
45 if (found.booked) {
46 return false;
47 }
48 return true;
49 }
Listing 9.22 The algorithm used to match cars, before using the strategy pattern.
Solution
Figure 9.15 shows how a strategy interface, Matcher, and its implementations are used to
handle the search for a car in the car registry. Note that CarRegistry has no dependency on
the implementations, only on the interface. As can be seen from the definition of the match
method, it is handed a list containing all available cars. This approach can be questioned, is it
201
Chapter 9 Polymorphism and Inheritance
not time consuming to read all cars from the database and place them in a list? Maybe yes, the
search could have been defined in the database query, in a way that only a matching car was
returned. To solve the problem that way, match would have needed a database connection for
sending the appropriate query to the database, instead of the list of all existing cars. There are,
however, reasons to solve the problem as in figure 9.15 instead. Above all, there is no database
yet. Also, even though the proposed solution is more time consuming than a database query, it
will probably not create problems, since the list can not be extremely long. It is unlikely that
the rental company has more than a few hundred available cars to choose from. Another thing
worth noting is that an available car is specified by a CarDTO object, not by the CarData
object used internally in CarRegistry. This is to maintain the distinction that CarData
is a a replacement for a database, it is not appropriate for use outside CarRegistry. The
Matcher interface is listed in listing 9.23.
Figure 9.15 The strategy case study. There are three different algorithms for matching a car in the registry against
a customer’s wishes.
1 /**
2 * Defines the ability to match existing cars with a searched
3 * car. This interface shall be implemented by a class that
4 * provides a matching algorithm.
5 */
6 public interface Matcher {
7 /**
8 * Searches the specified available cars for an instance
9 * matching the specified search criteria.
10 *
11 * @param searched Search criteria
12 * @param available Available cars
13 * @return A matching car, or <code>null</code> if none
14 * was found.
15 */
16 CarDTO match(CarDTO searched, List<CarDTO> available);
17 }
Listing 9.23 The Matcher interface, wich defines the matching algorithm.
202
Chapter 9 Polymorphism and Inheritance
Next, it is time to create the implementations. The existing search algorithm looks for a
car with all properties, except registration number, equal to the properties of the searched car.
However, searched properties equal to null or zero are ignored. Therefore, this algorithm is
called WildCardMatch. The algorithm that never ignores any property, but requires all prop-
erties except registration number to match, is called PerfectMatch. Finally, the algorithm
used to promote a particular car is called PromotingMatch. The first two can be found in the
accompanying GitHub repository [Code], only the last is listed here, listing 9.24. The registra-
tion number of the car to promote is specified in setCarToPromote, lines 21-23. The search
algorithm is, of course, written in the method match, defined by the implemented interface.
PerfectMatch is used as a fallback if the promoted car is different from the searched car in
all aspects (line 59). This is in order to not have to tell the customer “sorry, there is no car”,
even if there actually is one.
1 /**
2 * A <code>Matcher</code> that finds the car that shall be
3 * promoted, provided it has at least one property, except
4 * registration number, matching the search criteria. If it
5 * has not, performs a <code>PerfectMatch</code>.
6 */
7 public class PromotingMatch implements Matcher {
8 private String regNoOfCarToPromote;
9
10 PromotingMatch() {
11 }
12
13 /**
14 * Specify which car to promote.
15 *
16 * @param regNo The car with this registration number will
17 * be found by the matching algorithm, if it
18 * exists and has at least one property equal
19 * to the search criteria.
20 */
21 public void setCarToPromote(String regNo) {
22 this.regNoOfCarToPromote = regNo;
23 }
24
25 @Override
26 public CarDTO match(CarDTO searched,
27 List<CarDTO> available) {
28 for (CarDTO carToMatch : available) {
29 if (!regNoOfCarToPromote.
30 equals(carToMatch.getRegNo())) {
31 continue;
32 }
33 if (carToMatch.getPrice() != null &&
203
Chapter 9 Polymorphism and Inheritance
The last part of the strategy pattern is the client, CarRegistry. The use of the matching
algorithm can be found on line 25 in listing 9.25. Unfortunately, there is a dependency on the
implementation of the algorithm, not only on the definition, since the WildCardMatch object
is created on this line. It is still worth the effort to define the Matcher interface, since the
only change required to switch algorithm is to change WildCardMatch for the desired class
name. However, it is not at all as beautiful as the implementation of the logging algorithm in
section 9.2, where it was possible to change algorithm while the program was running. This
defect will be mitigated with the next pattern, factory.
1 /**
2 * Search for a car that is not booked, and that matches the
3 * specified search criteria.
4 *
5 * @param searchedCar This object contains the search
6 * criteria. Fields in the object that are
204
Chapter 9 Polymorphism and Inheritance
Comments
• All algorithms have the same public interface It is problematic that all con-
crete strategies implement the same interface, if different implementations of
the algorithm need different data. An example in the case study could be if
some matching algorithms need to call the database, while others do not. In
this case, a database connection must be passed to the match method, even
though some classes will never use it. This might seem a small problem, the
solution is to simply set the database connection parameter to null, when
using an algorithm that does not call the database. However, it is not high
cohesion, nor a very nice public interface, to have methods with parameters
they do not use. The bigger the public interface, and the more the algorithms
differ, the bigger this problem gets. Unfortunately, there is no solution except
to unify the algorithm’s public interfaces as much as possible.
205
Chapter 9 Polymorphism and Inheritance
Overview
Case Study
Now it is finally time to remove CarRegistry’s dependency on the concrete matching algo-
rithms, which has been troubling us since it was introduced on line 25 in listing 9.25. It is
not a good design that CarRegistry must be changed and recompiled, when the algorithm
206
Chapter 9 Polymorphism and Inheritance
matching a customers wishes against available cars is swapped. The whole purpose of using
the strategy pattern was to make CarRegistry independent of those algorithms.
Solution
1 /**
2 * A Factory that creates instances of the algorithm used
3 * for matching a customer’s wishes against available cars.
4 * All such instances must implement <code>Matcher</code>.
5 */
6 public class MatcherFactory {
7 private static final String MATCHER_CLASS_NAME_KEY =
8 "se.leiflindback.rentcar.matcher.classname";
9 private static final String CAR_TO_PROMOTE_KEY =
10 "se.leiflindback.rentcar.matcher.promote";
11
12 /**
13 * Returns a <code>Matcher</code> performing the default
14 * matching algorithm. The class name of the default
15 * <code>Matcher</code> implementation is read from the
16 * system property
17 * <code>se.leiflindback.rentcar.matcher.classname</code>
18 *
19 * @return The default matcher
20 * @throws ClassNotFoundException If unable to load the
21 * default matcher class.
207
Chapter 9 Polymorphism and Inheritance
Listing 9.26 The MatcherFactory class, which hands out a matching algorithm.
After the class name of the appropriate matcher is read from the system property, it still
remains to create an object of that class. Remember, from listing 9.8, that it is not required to
know the name of the class to be instantiated at compile time. It is possible to create an object
of a class, whose name is the content of a String variable. This is done on lines 33-34 in
listing 9.26.
The goal of removing all references to Matcher implementations from CarRegistry has
been reached now that the factory has been introduced, as can be seen from listing 9.27. This
has taken us closer to the point where we can change matching algorithm without having
to restart the program, but not all the way there. To completely reach that goal, we need a
mechanism to first change the value of the system property, and then to force the factory to
read the new value. How to do this is discussed in the Comments subsection, below.
1 /**
2 * Search for a car that is not booked, and that matches the
3 * specified search criteria.
4 *
5 * @param searchedCar This object contains the search
6 * criteria. Fields in the object that are
7 * set to <code>null</code> or zero are
8 * ignored.
9 * @return A description matching the searched car’s
10 * description if an unbooked car with the
208
Chapter 9 Polymorphism and Inheritance
Listing 9.27 The call from CarRegistry to the factory (line 27). There is no dependency on the
Matcher implementations
Comments
• How shall the factory know what to create? There are many different ways
to configure the factory, for example using a system property, as in the case
study above. Other alternatives are to have the factory read from a file, or to
create a user interface where the product can be specified. It might also be ap-
propriate to add a method to the factory, that can be used to define the product
to create, for example public void setDefaultProduct(Product
product).
No matter how the class name of the desired product is communicated to
the factory, there must also be a way to tell the factory to read it. It is not
enough to set product when the program is started, we also want to be able
to change during execution. If this change is to be initiated by something
outside the program, input must be accepted. There could for example be a
user interface which allows a system administrator to make the factory read
a new value. If the product’s class name is stored in the factory, it must have
a method that can be called to force it to re-read that class name.
209
Chapter 9 Polymorphism and Inheritance
The product created by the factory in the case study did not depend on pro-
gram state in any way. All matching algorithms were always allowed, no
matter which cars were available or what the customer wished. This is
not always the case, we could for example add another factory, that cre-
ates products for payment handling. If the customer pays cash, the fac-
tory shall create a CashPayment, if the customer pays with credit card, a
CreditCardPayment shall be created. In this case, there is only one al-
lowed product. That means the factory’s method that creates a product
must take a parameter that enables deciding which is the correct product.
The method definition could be for example public PaymentHandler
createPaymentHandler(PaymentType type).
1 Matcher matcher =
2 (Matcher)Class.forName(className).newInstance();
3 //read initialization data, for example from
4 //a file, and place it in a java.util.Map
5 //called initData.
6 matcher.init(initData);
7 return matcher;
• Products can be cached. The suggested solution above creates a new con-
crete product each time getDefaultMatcher is called. If the product is
210
Chapter 9 Polymorphism and Inheritance
• Who creates the factory itself? In listing 9.27, line 27, CarRegistry
creates a new instance of the factory each time it is needed. That is fine in
the case study program, but what if it is time consuming to create the factory,
maybe because it creates cached instances of concrete products as suggested
in the previous bullet? And what if the factory reads a configuration file as
suggested above, which is also time consuming? If, for any reason, it is not
appropriate to create new factory instances, there must be a way to guarantee
that there is only one, single, instance. There must also be a way to share
this instance between all methods where it is needed. This is the purpose of
the next pattern.
To block instantiation, the constructor of the critical object is made private. That means it can
only be called by the class itself, no other class can create instances. Next, one single instance
is created by the class itself, and saved in a static field. Last, a method is written, which hands
out this sole instance. A class with these properties is called a singleton. The class diagram in
figure 9.19a illustrates these features. Remember that underlining a member means it is static.
Since any singleton, by definition, has such a static method and field, and private constructor,
they are sometimes omitted and instead indicated with a stereotype, as in figure 9.19b.
Case Study
As a case study of the singleton pattern, consider the factory class created above. A factory is
often a candidate to become a singleton, especially if the objects it creates shall be cached.
211
Chapter 9 Polymorphism and Inheritance
(a) (b)
Solution
The solution in listing 9.29 is pretty straightforward. The static member holding the only
existing instance is on line seven, the static method handing out this instance is on lines 12-14
and the private constructor on lines 16-17. Listing 9.30 shows how the client, CarRegistry,
calls getFactory on line 12, in order to retrieve the only existing instance of the singleton.
1 /**
2 * A Singleton that creates instances of the algorithm used
3 * for matching a customer’s wishes against available cars.
4 * All such instances must implement <code>Matcher</code>.
5 */
6 public class MatcherFactory {
7 private static final MatcherFactory MATCHER_FACTORY =
8 new MatcherFactory();
9 /**
10 * @return The only instance of this singleton.
11 */
12 public static MatcherFactory getFactory() {
13 return MATCHER_FACTORY;
14 }
15
16 private MatcherFactory() {
17 }
18
19 // The getDefaultMatcher method has not been changed from
20 // listing 9.26.
21 }
Listing 9.29 The MatcherFactory class, from listing 9.26, after it has been turned into a single-
ton.
212
Chapter 9 Polymorphism and Inheritance
4 if (!car.booked) {
5 allCars.add(new CarDTO(car.regNo,
6 new Amount(car.price), car.size,
7 car.AC, car.fourWD, car.color,
8 car.booked));
9 }
10 }
11 try {
12 return MatcherFactory.getFactory().
13 getDefaultMatcher().match(searchedCar,
14 allCars);
15 } catch (ClassNotFoundException | InstantiationException |
16 IllegalAccessException ex) {
17 throw new CarRegistryException(
18 "Unable to instantiate matcher", ex);
19 }
20 }
Listing 9.30 CarRegistry retrieves the factory instance and calls getDefaultMatcher. The
only difference from listing 9.27 is line 12.
Comments
• Why not make all members static, instead of creating a singleton? This is
a good question that is often raised. In fact, there are many reasons, that may
not be obvious at first thought. Maybe the best reason is that a singleton is
likely to have state in the form of instance variables. If all methods are static,
then also all fields must be static, otherwise they can not be accessed by the
static methods. Already here we are giving up object orientation, but it easily
gets worse. The singleton probably has references to other objects, and when
methods in those objects are called, there is a risk that the make everything
static disease spreads also to those. Also, even if all members are made static,
we still need to add a private constructor to prohibit instantiating objects. In
fact, the question should probably be posed the opposite way, why make
something static in an object-oriented program, if it can be avoided? There
are also many other reasons, for example it is easier to change a singleton into
an ordinary object. Also, static methods can not be changed by inheritance,
and static fields can not be serialized.
213
Chapter 9 Polymorphism and Inheritance
• When is the singleton instance created? The case study singleton instance
is created when line seven in listing 9.29 is executed, but when is this? It
is when static fields are initialized, which is when a class file is loaded by
the JVM. Exactly when this happens varies, but the latest time a class can be
loaded is when the JVM encounters the class name in the program.
214
Chapter 9 Polymorphism and Inheritance
Figure 9.22 When called, the composite does not perform the task, but instead calls the concrete tasks it contains.
Case Study
Consider the car matching algorithm provided by PromotingMatch, which is listed in list-
ing 9.24. This algorithm tries to match the searched car with the car that shall be pro-
moted, and, if it fails, asks the PerfectMatch algorithm for a car (line 59). This is ac-
tually a hard-coded combination of two algorithms. Now, what if the fallback algorithm of
PromotingMatch shall be changed? In this case it will be necessary to change the code on
line 59 in PromotingMatch. And what if a new combining algorithm is introduced? Say
for example DiscountMatch, which tries to give the customer a good deal by finding the car
with the highest discount, and if no discount is found uses another algorithm. Maybe it shall
be possible to change the fallback algorithm of both DiscountMatch and PromotingMatch
at runtime. And maybe DiscountMatch uses PromotingMatch as fallback, or vice versa.
All these situations prove it is a very bad solution to hard-code combinations of algorithms, as
was done in PromotingMatch. Instead, a composite must be used.
Solution
Since all concrete algorithms shall be independent of each other, the hard-coded fallback al-
gorithm in PromotingMatch is removed. All that is needed for this is to change line 59 in
listing 9.24, to return null if no car was found. This indicates that the algorithm did not find
a match.
The next step is to introduce a composite, which can be seen in listing 9.31. Note that this
class implements the same interface, Matcher, as the other matching algorithms. However,
no car matching is performed. Instead, match, which is supposed to search for cars, calls all
wrapped algorithms. The results can be combined in any way. Here, the search is interrupted
when an algorithm finds a car, and that car is then returned.
What about the empty init method on lines 53-55? This is the init method dis-
cussed above, in the section Try to make the factory independent of concrete products, on
page 210. The purpose is to completely remove all hard-coded class names of Matcher
implementations from the entire code. The addition of this method makes it possible to
215
Chapter 9 Polymorphism and Inheritance
initialize PromotingMatch with the registration number of the car to promote, without
mentioning the name of that class in MatcherFactory. Exactly how this is done can
be seen in, se.leiflindback.oodbook.rentcarWithExAndDesPat.integration.
matchingWithComposite.MatcherFactory, in the accompanying GitHub repository
[Code].
1 /**
2 * A <code>Matcher</code>, which performs multiple matching
3 * algorithms. All matching algorithms added to this
4 * composite are executed, in the same order they were added,
5 * until an algorithm finds a car. Execution is stopped when
6 * an algorithm returns a non-null value.
7 */
8 class CompositeMatcher implements Matcher {
9 private List<Matcher> matchingAlgorithms =
10 new ArrayList<>();
11
12 CompositeMatcher() {
13 }
14
15 /**
16 * Invokes all matching algorithms added to this
17 * composite, in the same order they were added,
18 * until an algorithm finds a car. When a matching
19 * algorithm has found a car, that car is returned, and
20 * no more algorithms are called.
21 *
22 * @param searched Search criteria
23 * @param available Available cars
24 * @return A matching car, or <code>null</code> if none
25 * was found.
26 */
27 @Override
28 public CarDTO match(CarDTO searched,
29 List<CarDTO> available) {
30 for (Matcher matcher : matchingAlgorithms) {
31 CarDTO found = matcher.match(searched,
32 available);
33 if (found != null) {
34 return found;
35 }
36 }
37 return null;
38 }
39
40 /**
216
Chapter 9 Polymorphism and Inheritance
Listing 9.31 CompositeMatcher wraps the concrete matchers which are passed to
addMatcher on lines 49-51. The match method calls these concrete matchers in the order they
were added, until a matcher finds a car.
How is the composite populated with Matchers? This is done by the fac-
tory. When it is asked to produce a concrete product, it reads the system property
se.leiflindback.rentcar.matcher.classname, just as previously. The difference
now, is that this property may contain not just one class name of a concrete matcher, but a
comma-separated list of such names. If more than one class is mentioned in the property, the
factory creates a CompositeMatcher, and adds all specified classes to it. The private method
responsible for creating the composite can be seen in listing 9.32.
Comments
217
Chapter 9 Polymorphism and Inheritance
it might very well be mentioned in the latter case. It can be argued that
CompositeMatcher is, in fact, part of the infrastructure in the code just
written. There is only one composite, and it is used exactly the same way
whenever algorithms are to be combined.
Overview
This pattern tells us to create a template, which, just like the invitation card in fig-
ure 9.23, contains everything common for all situations, and leaves out only the specific
parts. The template will be an abstract class, TaskTemplate in figure 9.24a, containing
a method, performTask, with the common code. This method calls an abstract method,
doPerformTask, when it is time to execute the part of the code that differs between con-
crete implementations of the task. This call will arrive at one of the concrete subclasses,
ConcreteTaskA in the example in figure 9.24b, which has overridden the abstract method
and provided an implementation.
218
Chapter 9 Polymorphism and Inheritance
(a) Code that is common for performing the (b) The performTask method in the ab-
task of the two concrete classes, is placed stract template class calls a concrete imple-
in performTask, in the abstract template mentation of doPerformTask, when it
class. is time to execute code that differs between
concrete implementations.
Case Study
The case study concerns the display showing the number of rented cars, which was created as
a case study of the observer pattern. This display shows how many cars of different types have
currently been rented. The program shall now be extended to include two such displays, one
with a text view and one with a GUI view.
Solution
The source code of the display class can be seen in listing 9.18. The overridden method from
the RentalObserver interface, newRental on lines 20-24, calls two private methods. The
first, addNewRental, stores information about the latest rental, and is identical for the text and
GUI views. The second, printCurrentState, is different in the two views. This means that
newRental can be the same in both views, and also addNewRental. printCurrentState,
on the other hand, must be promoted to protected visibility, and overridden in the two
views. Listing 9.33 show the abstract template class, RentedCarsDisplay. Note the abstract
protected method on lines 38-39. Listings 9.34 and 9.35 show the two concrete views. They
contain only the implementations of printCurrentState, all other code is in the template
superclass. Finally, listing 9.36 shows part of the main view, View, where the concrete classes
are created.
1 /**
2 * Shows a running total of rented cars of each type.
3 */
4 public abstract class RentedCarsDisplay implements
5 RentalObserver {
6 private Map<CarDTO.CarType, Integer> noOfRentedCars =
7 new HashMap<>();
8
9 /**
219
Chapter 9 Polymorphism and Inheritance
Listing 9.33 The template class, which leaves the adaptable parts in an abstract protected method,
lines 38-39
1 /**
2 * Prints the number of rented cars on the console.
3 */
4 public class ConsoleRentedCarsDisplay extends
5 RentedCarsDisplay {
6 /**
7 * Prints the number of rented cars of each type on the
8 * console.
9 */
10 @Override
11 protected void printCurrentState(
220
Chapter 9 Polymorphism and Inheritance
1 /**
2 * Shows a GUI with the number of rented cars.
3 */
4 public class GuiRentedCarsDisplay extends RentedCarsDisplay {
5 private Display display;
6
7 /**
8 * Starts the GUI.
9 */
10 public GuiRentedCarsDisplay() {
11 new Thread(() -> {
12 Application.launch(Display.class, null);
13 }).start();
14 this.display = Display.getDisplay();
15 }
16
17 /**
18 * Shows a GUI with the number of rented cars of
19 * each type.
20 */
21 @Override
22 protected void printCurrentState(
23 Map<CarDTO.CarType, Integer> noOfRentedCars) {
24 display.updateStatsList(noOfRentedCars);
25 }
26
27 // Code for GUI handling.
28 }
Listing 9.35 A concrete implementation of the template. It contains only code that is specific for
this particular implemenation
221
Chapter 9 Polymorphism and Inheritance
1 /**
2 * This program has no view, instead, this class is a
3 * placeholder for the entire view.
4 */
5 public class View {
6 private Controller contr;
7 private ErrorMessageHandler errorMsgHandler =
8 new ErrorMessageHandler();
9 private LogHandler logger = LogHandler.getLogger();
10
11 /**
12 * Creates a new instance.
13 *
14 * @param contr The controller that is used for all
15 * operations.
16 */
17 public View(Controller contr) throws IOException {
18 this.contr = contr;
19 contr.addRentalObserver(
20 new ConsoleRentedCarsDisplay());
21 contr.addRentalObserver(new GuiRentedCarsDisplay());
22 }
23
24 // More methods, not relevant for this listing.
25 }
Comments
• More abstract methods In all examples mentioned here, there was only one
abstract method in the template class. This is not a requirement, we are free
to add any number of abstract methods, consider for example the template
class delineated in listing 9.37
1 /**
2 * An example of a template class with more than one abstract
3 * method.
4 */
5 public abstract class TemplateWithMoreAbstractMethods {
6 public void theTemplateMethod() {
7 try {
8 performSomeTask();
9 } catch (Exception e) {
222
Chapter 9 Polymorphism and Inheritance
10 errorHandling();
11 } finally {
12 cleanup();
13 }
14 }
15
16 protected abstract void performSomeTask();
17
18 protected abstract void errorHandling();
19
20 protected abstract void cleanup();
21 }
Listing 9.37 A template class with more than one abstract method
223
Part III
Appendices
224
Appendix A
English-Swedish Dictionary
This appendix contains translations to Swedish of some English terms in the text. Be aware
that these translations are terms commonly used in software development, and have a defined
meaning. To know a general translation because of skills in English language is not enough,
exactly the correct term must be used, not a synonym. Having said that, it is also important to
point out that there is no universal agreement on these terms, do not be surprised when finding
other words meaning the same thing.
English Swedish
analysis . . . . . . . . . . . . . . . . . . . analys
architectural pattern . . . . . . . . arkitekturellt mönster
architecture . . . . . . . . . . . . . . . arkitektur
class diagram . . . . . . . . . . . . . . klassdiagram
code convention . . . . . . . . . . . kodkonvention
communication diagram . . . . kommunikationsdiagram
design . . . . . . . . . . . . . . . . . . . . design
design pattern . . . . . . . . . . . . . designmönster
domain model . . . . . . . . . . . . . domänmodell
encapsulation . . . . . . . . . . . . . . inkapsling
entity . . . . . . . . . . . . . . . . . . . . . entitet
enumeration . . . . . . . . . . . . . . . uppräkningsbar typ
high cohesion . . . . . . . . . . . . . hög sammanhållning
implement . . . . . . . . . . . . . . . . implementera
instance . . . . . . . . . . . . . . . . . . . instans
layer . . . . . . . . . . . . . . . . . . . . . . lager
low coupling . . . . . . . . . . . . . . låg koppling
modifier . . . . . . . . . . . . . . . . . . modifierare
overload . . . . . . . . . . . . . . . . . . överlagra
override . . . . . . . . . . . . . . . . . . . omdefiniera
package diagram . . . . . . . . . . . paketdiagram
pattern . . . . . . . . . . . . . . . . . . . . mönster
225
Appendix A English-Swedish Dictionary
English Swedish
refactoring . . . . . . . . . . . . . . . . omstrukturering, refaktorering, refaktorisering
sequence diagram . . . . . . . . . . sekvensdiagram
system operation . . . . . . . . . . . systemoperation
system sequence diagram . . . systemsekvensdiagram
visibility . . . . . . . . . . . . . . . . . . åtkomst
226
Appendix B
Class Diagram
227
Appendix B UML Cheat Sheet
228
Appendix B UML Cheat Sheet
Sequence Diagram
229
Appendix B UML Cheat Sheet
Communication Diagram
230
Appendix B UML Cheat Sheet
231
Appendix C
1 package se.kth.ict.oodbook.design.uml;
2
3 public class AClass {
4 public void aMethod(int aParam) {
5 }
6 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class AnotherClass {
4 private static int aStaticAttribute;
5
6 public static String aStaticMethod() {
7 }
8 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class YetAnotherClass {
4 private int privateAttribute;
5 public int publicAttribute;
232
Appendix C Implementations of UML Diagrams
6
7 private String privateMethod() {
8 }
9
10 public int publicMethod() {
11 }
12 }
1 package somePackage;
Listing C.4
Java code im-
plementing the
somePackage
package in figure
5.2
1 package someOtherPackage;
1 package se.kth.ict.oodbook.design.uml;
2
3 public class SomeClass {
4 private OtherClass otherObj;
5
6 public void firstMethod() {
233
Appendix C Implementations of UML Diagrams
7 otherObj.aMethod();
8 methodInSelf();
9 }
10
11 public void someMethod() {
12 }
13
14 private void methodInSelf() {
15 }
16 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class OtherClass {
4 private SomeClass someObj;
5
6 public void aMethod() {
7 someObj.someMethod();
8 ThirdClass newObj = new ThirdClass();
9 }
10 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class ThirdClass {
4 public ThirdClass() {
5 }
6 }
1 package se.kth.ict.oodbook.design.uml;
234
Appendix C Implementations of UML Diagrams
2
3 public class A {
4 private B b;
5 // Somewhere in some method the following call is made:
6 // b.met1();
7 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class B {
4 public void met1() {
5 C.met2();
6 }
7 /*
8 * Code illustrated in a sequence diagram named ’SomeTask’.
9 */
10 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class C {
4 public static void met2() {
5
6 }
7 /*
8 * Code illustrated in a sequence diagram named ’SomeTask’.
9 */
10 }
1 package se.kth.ict.oodbook.design.uml;
235
Appendix C Implementations of UML Diagrams
2
3 public class ClassA {
4 private ClassB objB;
5 private ClassE objE;
6
7 public void metF() {
8 }
9 /* The following lines appear somwhere in the code, in the
10 * order they are written here.
11 * objB.metA(2);
12 * int retVal = objE.metD();
13 * objE.metE();
14 */
15 }
Listing C.12 Java code implementing the ClassA class in figure 5.5
1 package se.kth.ict.oodbook.design.uml;
2
3 public class ClassB {
4 private ClassC objC;
5
6 public void metA(int aParam) {
7 objC.metB();
8 ClassD objD = new ClassD();
9 }
10 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class ClassC {
4 public void metB() {
5 }
6 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class ClassD {
4 public ClassD() {
5 myMethod();
236
Appendix C Implementations of UML Diagrams
6 }
7
8 private void myMethod() {
9 }
10 }
237
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.uml;
2
3 public class ClassE {
4 private ClassA objA;
5
6 public void metE() {
7 objA.metF();
8 }
9
10 public int metD() {
11 return 0;
12 }
13 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class ClassF {
4 private int count;
5 private ClassG classG;
6 private ClassH classH;
7
8 /* The following code appears somewhere, in some method:
9 * if (count == 3) {
10 * classG.aMethod();
11 * } else {
12 * classH.aMethod();
13 * }
14 */
15 }
Listing C.17 Java code implementing the ClassF class in figure 5.6
1 package se.kth.ict.oodbook.design.uml;
2
238
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.uml;
2
3 public class ClassH {
4 private int n;
5 private ClassK classK;
6
7 public void aMethod() {
8 for (int i = 1; i <= n; i++) {
9 classK.aMet();
10 }
11 }
12 }
1 package se.kth.ict.oodbook.design.uml;
2
3 public class ClassK {
4 public void aMet() {
5 }
6 }
1 package se.kth.ict.oodbook.design.cohesion;
2
3 import java.util.List;
4
239
Appendix C Implementations of UML Diagrams
5 /**
6 * Represents an employee.
7 */
8 public class BadDesignEmployee {
9 private String name;
10 private Address address;
11 private Amount salary;
12
13 /**
14 * Changes the salary of <code>employee</code> to
15 * <code>newSalary</code>.
16 *
17 * @param employee The <code>Employee</code> whose salary will be
18 * changed.
19 * @param newSalary The new salary of <code>employee</code>.
20 */
21 public void changeSalary(BadDesignEmployee employee,
22 Amount newSalary) {
23 }
24
25 /**
26 * Returns a list with all employees working in the same
27 * department as this employee.
28 */
29 public List<BadDesignEmployee> getAllEmployees() {
30 }
31 }
Listing C.21 Java code implementing the UML diagram in figure 5.9a
1 package se.kth.ict.oodbook.design.cohesion;
2
3 /**
4 * Represents an employee.
5 */
6 public class Employee {
7 private String name;
8 private Address address;
9 private Amount salary;
10
11 /**
12 * Changes the salary to <code>newSalary</code>.
13 *
14 * @param newSalary The new salary.
15 */
16 public void changeSalary(Amount newSalary) {
240
Appendix C Implementations of UML Diagrams
17 this.salary = newSalary;
18 }
19 }
Listing C.22 Java code implementing the Employee class in figure 5.9b
1 package se.kth.ict.oodbook.design.cohesion;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * Represents a department.
8 */
9 public class Department {
10 private String name;
11 private List<Employee> employees = new ArrayList<>();
12
13 /**
14 * Returns a list with all employees working in this department.
15 */
16 public List<Employee> getEmployees() {
17 return employees;
18 }
19 }
Listing C.23 Java code implementing the Department class in figure 5.9b
1 package se.kth.ict.oodbook.design.cohesion;
2
3 /**
4 * Represents a car.
5 */
6 public class BadDesignCar {
7 private String regNo;
241
Appendix C Implementations of UML Diagrams
Listing C.24 Java code implementing the BadDesignCar class in figure 5.10a
1 package se.kth.ict.oodbook.design.cohesion;
2
3 /**
4 * Represents a car.
5 */
6 public class Car {
7 private String regNo;
8 private Person owner;
9 private Engine engine;
10 private Radio radio;
11
242
Appendix C Implementations of UML Diagrams
12 /**
13 * Returns the registration number of this car.
14 */
15 public String getRegNo() {
16 return regNo;
17 }
18 }
Listing C.25 Java code implementing the Car class in figure 5.10b
1 package se.kth.ict.oodbook.design.cohesion;
2
3 /**
4 * Represents a car radio.
5 */
6 public class Radio {
7 private String ownersPreferredStation;
8 /**
9 * Sets the radio to the specified station.
10 * @param station The station to which to listen.
11 */
12 public void changeStation(String station) {
13 }
14 }
Listing C.26 Java code implementing the Radio class in figure 5.10b
1 package se.kth.ict.oodbook.design.cohesion;
2
3 /**
4 * Represents a car engine.
5 */
6 public class Engine {
7 /**
8 * Accelerates the car.
9 */
10 public void accelerate() {
11 }
12
13 /**
14 * Breaks the car.
15 */
16 public void brake() {
17 }
18 }
243
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.coupling;
2
3 public class HighCouplingOrder {
4 private HighCouplingCustomer customer;
5 private HighCouplingShippingAddress shippingAddress;
6 }
1 package se.kth.ict.oodbook.design.coupling;
2
3 class HighCouplingCustomer {
4 private HighCouplingShippingAddress shippingAddress;
5 }
1 package se.kth.ict.oodbook.design.coupling;
2
3 class HighCouplingShippingAddress {
4 }
1 package se.kth.ict.oodbook.design.coupling;
2
3 public class Order {
4 private Customer customer;
5 }
Listing C.31 Java code implementing the Order class in figure 5.12b
1 package se.kth.ict.oodbook.design.coupling;
244
Appendix C Implementations of UML Diagrams
2
3 class Customer {
4 private ShippingAddress shippingAddress;
5 }
Listing C.32 Java code implementing the Customer class in figure 5.12b
1 package se.kth.ict.oodbook.design.coupling;
2
3 class ShippingAddress {
4 }
Listing C.33 Java code implementing the ShippingAddress class in figure 5.12b
1 package se.kth.ict.oodbook.design.coupling;
2
3 class HighCouplingBooking {
4 }
1 package se.kth.ict.oodbook.design.coupling;
2
3 class HighCouplingGuest {
4 }
1 package se.kth.ict.oodbook.design.coupling;
2
3 public class HighCouplingHotel {
4 private HighCouplingBooking booking;
5 private HighCouplingGuest guest;
6 private HighCouplingAddress address;
7 private HighCouplingFloor floor;
8 private HighCouplingRoom room;
9 }
245
Appendix C Implementations of UML Diagrams
246
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.coupling;
2
3 class HighCouplingAddress {
4 }
1 package se.kth.ict.oodbook.design.coupling;
2
3 class HighCouplingFloor {
4 }
1 package se.kth.ict.oodbook.design.coupling;
2
3 class HighCouplingRoom {
4 }
Listing C.39 Java code implementing the HighCouplingRoom class in figure 5.13a
1 package se.kth.ict.oodbook.design.coupling;
2
3 class Booking {
4 private Guest guest;
5 }
Listing C.40 Java code implementing the Booking class in figure 5.13b
1 package se.kth.ict.oodbook.design.coupling;
2
3 class Guest {
4 }
Listing C.41 Java code implementing the Guest class in figure 5.13b
1 package se.kth.ict.oodbook.design.coupling;
2
3 public class Hotel {
4 private Booking booking;
5 private Address address;
247
Appendix C Implementations of UML Diagrams
Listing C.42 Java code implementing the Hotel class in figure 5.13b
1 package se.kth.ict.oodbook.design.coupling;
2
3 class Address {
4 }
Listing C.43 Java code implementing the Address class in figure 5.13b
1 package se.kth.ict.oodbook.design.coupling;
2
3 class Floor {
4 private Room room;
5 }
Listing C.44 Java code implementing the Floor class in figure 5.13b
1 package se.kth.ict.oodbook.design.coupling;
2
3 class Room {
4 }
Listing C.45 Java code implementing the Room class in figure 5.13b
248
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.architecture.packPriv;
2
3 /**
4 * Illustrates package private field and method. Note that it is
5 * not required to write javadoc for these, since they are not
6 * part of the public interface.
7 */
8 public class PackPriv {
9 int packagePrivateAtribute;
10
11 void packagePrivateMethod() {
12 }
13 }
Listing C.46 Java code implementing the PackPriv class in figure 5.15
1 package se.kth.ict.oodbook.architecture.mvc.controller;
2
3 /**
4 * This is the application’s controller. All calls from view to model
5 * pass through here.
6 */
7 public class Controller {
8 /**
9 * A system operation, which means it appears in the system sequence
10 * diagram.
11 */
12 public void systemOperation1() {
13 }
14
15 /**
16 * A system operation, which means it appears in the system sequence
17 * diagram.
18 */
19 public void systemOperation2() {
20 }
21 }
Listing C.47 Java code implementing the Controller class in figure 5.18
249
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.architecture.mvc.view;
2
3 import se.kth.ict.oodbook.architecture.mvc.controller.Controller;
4
5 /**
6 * A class in the view.
7 */
8 public class ClassInView {
9 private Controller contr;
10
11 //Somewhere in some method.
12 contr.systemOperation1();
13 }
Listing C.48 Java code implementing the ClassInView class in figure 5.19
1 package se.kth.ict.oodbook.architecture.mvc.controller;
2
3 import se.kth.ict.oodbook.architecture.mvc.model.OtherClassInModel;
4 import se.kth.ict.oodbook.architecture.mvc.model.SomeClassInModel;
5
6 /**
7 * This is the application’s controller. All calls from view to model
8 * pass through here.
9 */
10 public class Controller {
11 private SomeClassInModel scim;
12 private OtherClassInModel ocim;
13
14 /**
15 * A system operation, which means it appears in the system sequence
16 * diagram.
17 */
18 public void systemOperation1() {
19 scim.aMethod();
20 ocim.aMethod();
21 }
22 }
Listing C.49 Java code implementing the Controller class in figure 5.19
250
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.architecture.mvc.model;
2
3 /**
4 * A class in the model, performing some business logic.
5 */
6 public class SomeClassInModel {
7
8 /**
9 * Performs some business logic.
10 */
11 public void aMethod() {
12 }
13 }
Listing C.50 Java code implementing the SomeClassInModel class in figure 5.19
1 package se.kth.ict.oodbook.architecture.mvc.model;
2
3 /**
4 * A class in the model, performing some business logic.
5 */
6 public class OtherClassInModel {
7
8 /**
9 * Performs some business logic.
10 */
11 public void aMethod() {
12 }
13 }
Listing C.51 Java code implementing the OtherClassInModel class in figure 5.19
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.Car;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
251
Appendix C Implementations of UML Diagrams
9 */
10 public class View {
11 // Somewhere in the code. Note that the arguments to the
12 // Car constructor are not specified in the UML diagram.
13 Car searchedCar = new Car(0, null, false, false,
14 null, null);
15 Car foundCar = contr.searchMatchingCar(searchedCar);
16 }
17
18 }
Listing C.52 Java code implementing the View class in figure 5.25
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.Car;
4
5 /**
6 * This is the application’s only controller class. All calls to
7 * the model pass through here.
8 */
9 public class Controller {
10 public Car searchMatchingCar(Car searchedCar) {
11 }
12 }
Listing C.53 Java code implementing the Controller class in figure 5.25
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains information about one particular car.
5 */
6 public class Car {
7
8 private int price;
9 private String size;
10 private boolean AC;
11 private boolean fourWD;
12 private String color;
13 private String regNo;
14
15 /**
16 * Creates a new instance representing a particular car.
17 *
252
Appendix C Implementations of UML Diagrams
253
Appendix C Implementations of UML Diagrams
64
65 /**
66 * Get the value of AC
67 *
68 * @return the value of AC
69 */
70 public boolean isAC() {
71 return AC;
72 }
73
74 /**
75 * Get the value of size
76 *
77 * @return the value of size
78 */
79 public String getSize() {
80 return size;
81 }
82
83 /**
84 * Get the value of price
85 *
86 * @return the value of price
87 */
88 public int getPrice() {
89 return price;
90 }
91
92 }
Listing C.54 Java code implementing the Car class in figure 5.25
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
254
Appendix C Implementations of UML Diagrams
Listing C.55 Java code implementing the View class in figure 5.26
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4
5 /**
6 * This is the application’s only controller class. All calls to
7 * the model pass through here.
8 */
9 public class Controller {
10 public CarDTO searchMatchingCar(CarDTO searchedCar) {
11 }
12 }
Listing C.56 Java code implementing the Controller class in figure 5.26
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains information about one particular car.
5 */
6 public class CarDTO {
7
8 private int price;
9 private String size;
10 private boolean AC;
11 private boolean fourWD;
12 private String color;
13 private String regNo;
14
15 /**
16 * Creates a new instance representing a particular car.
17 *
18 * @param price The price paid to rent the car.
255
Appendix C Implementations of UML Diagrams
256
Appendix C Implementations of UML Diagrams
65 /**
66 * Get the value of AC
67 *
68 * @return the value of AC
69 */
70 public boolean isAC() {
71 return AC;
72 }
73
74 /**
75 * Get the value of size
76 *
77 * @return the value of size
78 */
79 public String getSize() {
80 return size;
81 }
82
83 /**
84 * Get the value of price
85 *
86 * @return the value of price
87 */
88 public int getPrice() {
89 return price;
90 }
91
92 }
Listing C.57 Java code implementing the CarDTO class in figure 5.26
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
11 // Somewhere in the code. Note that the arguments to the
257
Appendix C Implementations of UML Diagrams
Listing C.58 Java code implementing the View class in figure 5.27
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4
5 /**
6 * This is the application’s only controller class. All calls to
7 * the model pass through here.
8 */
9 public class Controller {
10 public CarDTO searchMatchingCar(CarDTO searchedCar) {
11 return carRegistry.findCar(searchedCar);
12 }
13 }
Listing C.59 Java code implementing the Controller class in figure 5.27
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains information about one particular car.
5 */
6 public class CarDTO {
7
8 private int price;
9 private String size;
10 private boolean AC;
11 private boolean fourWD;
12 private String color;
13 private String regNo;
14
15 /**
16 * Creates a new instance representing a particular car.
17 *
18 * @param price The price paid to rent the car.
258
Appendix C Implementations of UML Diagrams
259
Appendix C Implementations of UML Diagrams
65 /**
66 * Get the value of AC
67 *
68 * @return the value of AC
69 */
70 public boolean isAC() {
71 return AC;
72 }
73
74 /**
75 * Get the value of size
76 *
77 * @return the value of size
78 */
79 public String getSize() {
80 return size;
81 }
82
83 /**
84 * Get the value of price
85 *
86 * @return the value of price
87 */
88 public int getPrice() {
89 return price;
90 }
91
92 }
Listing C.60 Java code implementing the CarDTO class in figure 5.27
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 public CarDTO findCar(CarDTO searchedCar) {
9 }
10 }
Listing C.61 Java code implementing the CarRegistry class in figure 5.27
260
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.startup;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.view.View;
6
7 /**
8 * Contains the <code>main</code> method. Performs all startup of
9 * the application.
10 */
11 public class Main {
12 public static void main(String[] args) {
13 CarRegistry carRegistry = new CarRegistry();
14 Controller contr = new Controller(carRegistry);
15 new View(contr);
16 }
17 }
Listing C.62 Java code implementing the Main class in figure 5.28
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 }
Listing C.63 Java code implementing the CarRegistry class in figure 5.28
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5
6 /**
7 * This is the application’s only controller class. All calls to
8 * the model pass through here.
9 */
10 public class Controller {
11 public Controller(CarRegistry carRegistry) {
261
Appendix C Implementations of UML Diagrams
12 }
13 }
Listing C.64 Java code implementing the Controller class in figure 5.28
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
11 public View(Controller contr) {
12 }
13 }
Listing C.65 Java code implementing the View class in figure 5.28
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
11 private Controller contr;
12
13 /**
14 * Creates a new instance.
15 *
16 * @param contr The controller that is used for all operations.
17 */
18 public View(Controller contr) {
19 }
20 }
262
Appendix C Implementations of UML Diagrams
Listing C.66 Java code implementing the View class in figure 5.29
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5
6 /**
7 * This is the application’s only controller class. All calls to
8 * the model pass through here.
9 */
10 public class Controller {
11 private CarRegistry carRegistry;
12
13 /**
14 * Creates a new instance.
15 *
16 * @param carRegistry Used to access the car data store.
17 */
18 public Controller(CarRegistry carRegistry) {
19 }
20
21 /**
22 * Search for a car matching the specified search criteria.
23 *
24 * @param searchedCar This object contains the search criteria.
25 * Fields in the object that are set to
26 * <code>null</code> or
27 * <code>false</code> are ignored.
28 * @return The best match of the search criteria.
29 */
30 public CarDTO searchMatchingCar(CarDTO searchedCar) {
31 }
32
33 /**
34 * Registers a new customer. Only registered customers can
35 * rent cars.
36 *
37 * @param customer The customer that will be registered.
38 */
39 public void registerCustomer(CustomerDTO customer) {
40 }
41 }
263
Appendix C Implementations of UML Diagrams
Listing C.67 Java code implementing the Controller class in figure 5.29
1 package se.kth.ict.oodbook.design.casestudy.startup;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.view.View;
6
7 /**
8 * Contains the <code>main</code> method. Performs all startup
9 * of the application.
10 */
11 public class Main {
12 public static void main(String[] args) {
13 }
14 }
Listing C.68 Java code implementing the Main class in figure 5.29
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 /**
9 * Creates a new instance.
10 */
11 public CarRegistry() {
12 }
13
14 /**
15 * Search for a car matching the specified search criteria.
16 *
17 * @param searchedCar This object contains the search criteria.
18 * Fields in the object that are set to
19 * <code>null</code> or
20 * <code>false</code> are ignored.
21 * @return The best match of the search criteria.
22 */
23 public CarDTO findCar(CarDTO searchedCar) {
24 }
264
Appendix C Implementations of UML Diagrams
25 }
Listing C.69 Java code implementing the CarRegistry class in figure 5.29
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains information about one particular car.
5 */
6 public class CarDTO {
7
8 private int price;
9 private String size;
10 private boolean AC;
11 private boolean fourWD;
12 private String color;
13 private String regNo;
14
15 /**
16 * Creates a new instance representing a particular car.
17 *
18 * @param price The price paid to rent the car.
19 * @param size The size of the car, e.g.,
20 * <code>medium hatchback</code>.
21 * @param AC <code>true</code> if the car has
22 * air condition.
23 * @param fourWD <code>true</code> if the car has four
24 * wheel drive.
25 * @param color The color of the car.
26 * @param regNo The car’s registration number.
27 */
28 public CarDTO(int price, String size, boolean AC,
29 boolean fourWD, String color, String regNo) {
30 }
31
32 /**
33 * Get the value of regNo
34 *
35 * @return the value of regNo
36 */
37 public String getRegNo() {
38 }
39
40 /**
41 * Get the value of color
42 *
265
Appendix C Implementations of UML Diagrams
Listing C.70 Java code implementing the CarDTO class in figure 5.29
1 package se.kth.ict.oodbook.design.casestudy.view;
266
Appendix C Implementations of UML Diagrams
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5 import se.kth.ict.oodbook.design.casestudy.model.AddressDTO;
6 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
7 import se.kth.ict.oodbook.design.casestudy.model.DrivingLicenseDTO;
8
9 /**
10 * This program has no view, instead, this class is a placeholder
11 * for the entire view.
12 */
13 public class View {
14 private Controller contr;
15
16 // Somewhere in the code. Note that the arguments to the
17 // DTO constructors are not specified in the UML
18 // diagram.
19 AddressDTO address = new AddressDTO("Storgatan 2", "12345",
20 "Hemorten");
21 DrivingLicenseDTO drivingLicense = new DrivingLicenseDTO(
22 "982193721937213");
23 CustomerDTO customer = new CustomerDTO("Stina", address,
24 drivingLicense);
25 contr.registerCustomer(customer);
26 }
Listing C.71 Java code implementing the View class in figure 5.30
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a post address.
5 */
6 public final class AddressDTO {
7 private final String street;
8 private final String zip;
9 private final String city;
10
11 /**
12 * Creates a new instance.
13 *
14 * @param street Street name and number.
15 * @param zip Zip code
16 * @param city City (postort)
17 */
18 public AddressDTO(String street, String zip, String city) {
267
Appendix C Implementations of UML Diagrams
19 this.street = street;
20 this.zip = zip;
21 this.city = city;
22 }
23
24 /**
25 * Get the value of city
26 *
27 * @return the value of city
28 */
29 public String getCity() {
30 return city;
31 }
32
33 /**
34 * Get the value of zip
35 *
36 * @return the value of zip
37 */
38 public String getZip() {
39 return zip;
40 }
41
42 /**
43 * Get the value of street
44 *
45 * @return the value of street
46 */
47 public String getStreet() {
48 return street;
49 }
50
51 }
Listing C.72 Java code implementing the AddressDTO class in figure 5.30
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a driving license
5 */
6 public class DrivingLicenseDTO {
7 private final String licenseNo;
8
9 /**
10 * Creates a new instance.
268
Appendix C Implementations of UML Diagrams
11 *
12 * @param licenseNo The driving license number.
13 */
14 public DrivingLicenseDTO(String licenseNo) {
15 this.licenseNo = licenseNo;
16 }
17
18 /**
19 * Get the value of licenseNo
20 *
21 * @return the value of licenseNo
22 */
23 public String getLicenseNo() {
24 return licenseNo;
25 }
26
27 }
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a customer of the car rental company.
5 */
6 public class CustomerDTO {
7 private final String name;
8 private final AddressDTO address;
9 private final DrivingLicenseDTO drivingLicense;
10
11 /**
12 * Creates a new instance.
13 *
14 * @param name The customer’s name.
15 * @param address The customer’s address.
16 * @param drivingLicense The customer’s driving license.
17 */
18 public CustomerDTO(String name, AddressDTO address,
19 DrivingLicenseDTO drivingLicense) {
20 this.name = name;
21 this.address = address;
22 this.drivingLicense = drivingLicense;
23 }
24
25 /**
269
Appendix C Implementations of UML Diagrams
Listing C.74 Java code implementing the CustomerDTO class in figure 5.30
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
6 import se.kth.ict.oodbook.design.casestudy.model.Rental;
7
8 /**
9 * This is the application’s only controller class. All calls to
10 * the model pass through here.
11 */
12 public class Controller {
13 /**
14 * Registers a new customer. Only registered customers can
15 * rent cars.
16 *
270
Appendix C Implementations of UML Diagrams
Listing C.75 Java code implementing the Controller class in figure 5.30
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5 import se.kth.ict.oodbook.design.casestudy.model.AddressDTO;
6 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
7 import se.kth.ict.oodbook.design.casestudy.model.DrivingLicenseDTO;
8
9 /**
10 * This program has no view, instead, this class is a placeholder
11 * for the entire view.
12 */
13 public class View {
14 private Controller contr;
15
16 // Somewhere in the code. Note that the arguments to the
17 // DTO constructors are not specified in the UML
18 // diagram.
19 AddressDTO address = new AddressDTO("Storgatan 2", "12345",
20 "Hemorten");
21 DrivingLicenseDTO drivingLicense = new DrivingLicenseDTO(
22 "982193721937213");
23 CustomerDTO customer = new CustomerDTO("Stina", address,
24 drivingLicense);
25 contr.registerCustomer(customer);
26 }
Listing C.76 Java code implementing the View class in figure 5.31
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a post address.
271
Appendix C Implementations of UML Diagrams
5 */
6 public final class AddressDTO {
7 private final String street;
8 private final String zip;
9 private final String city;
10
11 /**
12 * Creates a new instance.
13 *
14 * @param street Street name and number.
15 * @param zip Zip code
16 * @param city City (postort)
17 */
18 public AddressDTO(String street, String zip, String city) {
19 this.street = street;
20 this.zip = zip;
21 this.city = city;
22 }
23
24 /**
25 * Get the value of city
26 *
27 * @return the value of city
28 */
29 public String getCity() {
30 return city;
31 }
32
33 /**
34 * Get the value of zip
35 *
36 * @return the value of zip
37 */
38 public String getZip() {
39 return zip;
40 }
41
42 /**
43 * Get the value of street
44 *
45 * @return the value of street
46 */
47 public String getStreet() {
48 return street;
49 }
50
272
Appendix C Implementations of UML Diagrams
51 }
Listing C.77 Java code implementing the AddressDTO class in figure 5.31
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a driving license
5 */
6 public class DrivingLicenseDTO {
7 private final String licenseNo;
8
9 /**
10 * Creates a new instance.
11 *
12 * @param licenseNo The driving license number.
13 */
14 public DrivingLicenseDTO(String licenseNo) {
15 this.licenseNo = licenseNo;
16 }
17
18 /**
19 * Get the value of licenseNo
20 *
21 * @return the value of licenseNo
22 */
23 public String getLicenseNo() {
24 return licenseNo;
25 }
26
27 }
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a customer of the car rental company.
5 */
6 public class CustomerDTO {
7 private final String name;
8 private final AddressDTO address;
9 private final DrivingLicenseDTO drivingLicense;
10
11 /**
273
Appendix C Implementations of UML Diagrams
Listing C.79 Java code implementing the CustomerDTO class in figure 5.31
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
274
Appendix C Implementations of UML Diagrams
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
6 import se.kth.ict.oodbook.design.casestudy.model.Rental;
7
8 /**
9 * This is the application’s only controller class. All calls to
10 * the model pass through here.
11 */
12 public class Controller {
13 /**
14 * Registers a new customer. Only registered customers can
15 * rent cars.
16 *
17 * @param customer The customer that will be registered.
18 */
19 public void registerCustomer(CustomerDTO customer) {
20 rental = new Rental(customer);
21 }
22 }
Listing C.80 Java code implementing the Controller class in figure 5.31
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents one particular rental transaction, where one
5 * particular car is rented by one particular customer.
6 */
7 public class Rental {
8 private CustomerDTO customer;
9
10 /**
11 * Creates a new instance, representing a rental made by the
12 * specified customer.
13 *
14 * @param customer The renting customer.
15 */
16 public Rental(CustomerDTO customer) {
17 this.customer = customer;
18 }
19 }
Listing C.81 Java code implementing the Rental class in figure 5.31
275
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
11 private Controller contr;
12
13 /**
14 * Creates a new instance.
15 *
16 * @param contr The controller that is used for all operations.
17 */
18 public View(Controller contr) {
19 }
20 }
Listing C.82 Java code implementing the View class in figure 5.32
276
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5
6 /**
7 * This is the application’s only controller class. All calls to
8 * the model pass through here.
9 */
10 public class Controller {
11 private CarRegistry carRegistry;
12
13 /**
14 * Creates a new instance.
15 *
16 * @param carRegistry Used to access the car data store.
17 */
18 public Controller(CarRegistry carRegistry) {
19 }
20
21 /**
22 * Search for a car matching the specified search criteria.
23 *
24 * @param searchedCar This object contains the search criteria.
25 * Fields in the object that are set to
26 * <code>null</code> or
27 * <code>false</code> are ignored.
28 * @return The best match of the search criteria.
29 */
30 public CarDTO searchMatchingCar(CarDTO searchedCar) {
31 }
32
33 /**
34 * Registers a new customer. Only registered customers can
35 * rent cars.
36 *
37 * @param customer The customer that will be registered.
38 */
39 public void registerCustomer(CustomerDTO customer) {
40 }
41 }
Listing C.83 Java code implementing the Controller class in figure 5.32
1 package se.kth.ict.oodbook.design.casestudy.startup;
277
Appendix C Implementations of UML Diagrams
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.view.View;
6
7 /**
8 * Contains the <code>main</code> method. Performs all startup
9 * of the application.
10 */
11 public class Main {
12 public static void main(String[] args) {
13 }
14 }
Listing C.84 Java code implementing the Main class in figure 5.32
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 /**
9 * Creates a new instance.
10 */
11 public CarRegistry() {
12 }
13
14 /**
15 * Search for a car matching the specified search criteria.
16 *
17 * @param searchedCar This object contains the search
18 * criteria. Fields in the object that
19 * are set to <code>null</code> or
20 * <code>false</code> are ignored.
21 * @return The best match of the search criteria.
22 */
23 public CarDTO findCar(CarDTO searchedCar) {
24 }
25 }
Listing C.85 Java code implementing the CarRegistry class in figure 5.32
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
278
Appendix C Implementations of UML Diagrams
2
3 /**
4 * Contains information about one particular car.
5 */
6 public class CarDTO {
7
8 private int price;
9 private String size;
10 private boolean AC;
11 private boolean fourWD;
12 private String color;
13 private String regNo;
14
15 /**
16 * Creates a new instance representing a particular car.
17 *
18 * @param price The price paid to rent the car.
19 * @param size The size of the car, e.g.,
20 * <code>medium hatchback</code>.
21 * @param AC <code>true</code> if the car has
22 * air condition.
23 * @param fourWD <code>true</code> if the car has four
24 * wheel drive.
25 * @param color The color of the car.
26 * @param regNo The car’s registration number.
27 */
28 public CarDTO(int price, String size, boolean AC,
29 boolean fourWD, String color, String regNo) {
30 }
31
32 /**
33 * Get the value of regNo
34 *
35 * @return the value of regNo
36 */
37 public String getRegNo() {
38 }
39
40 /**
41 * Get the value of color
42 *
43 * @return the value of color
44 */
45 public String getColor() {
46 }
47
279
Appendix C Implementations of UML Diagrams
48 /**
49 * Get the value of fourWD
50 *
51 * @return the value of fourWD
52 */
53 public boolean isFourWD() {
54 }
55
56 /**
57 * Get the value of AC
58 *
59 * @return the value of AC
60 */
61 public boolean isAC() {
62 }
63
64 /**
65 * Get the value of size
66 *
67 * @return the value of size
68 */
69 public String getSize() {
70 }
71
72 /**
73 * Get the value of price
74 *
75 * @return the value of price
76 */
77 public int getPrice() {
78 }
79
80 }
Listing C.86 Java code implementing the CarDTO class in figure 5.32
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents one particular rental transaction, where one
5 * particular car is rented by one particular customer.
6 */
7 public class Rental {
8 private CustomerDTO customer;
9
10 /**
280
Appendix C Implementations of UML Diagrams
Listing C.87 Java code implementing the Rental class in figure 5.32
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a driving license
5 */
6 public class DrivingLicenseDTO {
7 private final String licenseNo;
8
9 /**
10 * Creates a new instance.
11 *
12 * @param licenseNo The driving license number.
13 */
14 public DrivingLicenseDTO(String licenseNo) {
15 }
16
17 /**
18 * Get the value of licenseNo
19 *
20 * @return the value of licenseNo
21 */
22 public String getLicenseNo() {
23 }
24
25 }
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a customer of the car rental company.
5 */
6 public class CustomerDTO {
281
Appendix C Implementations of UML Diagrams
Listing C.89 Java code implementing the CustomerDTO class in figure 5.32
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
282
Appendix C Implementations of UML Diagrams
Listing C.90 Java code implementing the AddressDTO class in figure 5.32
283
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5 import se.kth.ict.oodbook.design.casestudy.model.AddressDTO;
6 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
7 import se.kth.ict.oodbook.design.casestudy.model.DrivingLicenseDTO;
8
9 /**
10 * This program has no view, instead, this class is a placeholder
11 * for the entire view.
12 */
13 public class View {
14 private Controller contr;
15
16 //Somewhere in the code.
17 contr.bookCar(foundCar);
18 }
Listing C.91 Java code implementing the View class in figure 5.33
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.dbhandler.RentalRegistry;
6 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
7 import se.kth.ict.oodbook.design.casestudy.model.Rental;
8
9 /**
10 * This is the application’s only controller class. All calls to the
11 * model pass through here.
12 */
13 public class Controller {
14 private RentalRegistry rentalRegistry;
15 private Rental rental;
16
17 /**
18 * Books the specified car. After calling this method, the car
19 * can not be booked by any other customer. This method also
20 * permanently saves information about the current rental.
21 *
22 * @param car The car that will be booked.
284
Appendix C Implementations of UML Diagrams
23 */
24 public void bookCar(CarDTO car) {
25 rental.setRentedCar(car);
26 rentalRegistry.saveRental(rental);
27 }
28 }
Listing C.92 Java code implementing the Controller class in figure 5.33
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5
6 /**
7 * Represents one particular rental transaction, where one
8 * particular car is rented by one particular customer.
9 */
10 public class Rental {
11 private CarDTO rentedCar;
12 private CarRegistry carRegistry;
13 /**
14 * Specifies the car that was rented.
15 *
16 * @param rentedCar The car that was rented.
17 */
18 public void setRentedCar(CarDTO rentedCar) {
19 this.rentedCar = rentedCar;
20 carRegistry.bookCar(rentedCar);
21 }
22 }
Listing C.93 Java code implementing the Rental class in figure 5.33
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Rental;
4
5 /**
6 * Contains all calls to the data store with performed rentals.
7 */
8 public class RentalRegistry {
9 /**
10 * Saves the specified rental permanently.
11 *
285
Appendix C Implementations of UML Diagrams
Listing C.94 Java code implementing the RentalRegistry class in figure 5.33
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains all calls to the data store with cars that may
5 * be rented.
6 */
7 public class CarRegistry {
8 /**
9 * Books the specified car. After calling this method,
10 * the car can not be booked by any other customer.
11 *
12 * @param car The car that will be booked.
13 */
14 public void bookCar(CarDTO car) {
15 }
16 }
Listing C.95 Java code implementing the CarRegistry class in figure 5.33
1 package se.kth.ict.oodbook.design.casestudy.startup;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.view.View;
6
7 /**
8 * Contains the <code>main</code> method. Performs all startup of
9 * the application.
10 */
11 public class Main {
12 public static void main(String[] args) {
13 CarRegistry carRegistry = new CarRegistry();
14 RentalRegistry rentalRegistry = new RentalRegistry();
286
Appendix C Implementations of UML Diagrams
Listing C.96 Java code implementing the Main class in figure 5.34
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 }
Listing C.97 Java code implementing the CarRegistry class in figure 5.34
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Rental;
4
5 /**
6 * Contains all calls to the data store with performed rentals.
7 */
8 public class RentalRegistry {
9 }
Listing C.98 Java code implementing the RentalRegistry class in figure 5.34
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5
6 /**
7 * This is the application’s only controller class. All calls to
8 * the model pass through here.
9 */
10 public class Controller {
11 /**
12 * Creates a new instance.
13 *
287
Appendix C Implementations of UML Diagrams
Listing C.99 Java code implementing the Controller class in figure 5.34
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
11 /**
12 * Creates a new instance.
13 *
14 * @param contr The controller that is used for all
15 * operations.
16 */
17 public View(Controller contr) {
18 }
19 }
Listing C.100 Java code implementing the View class in figure 5.34
1 package se.kth.ict.oodbook.design.casestudy.startup;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.view.View;
6
7 /**
8 * Contains the <code>main</code> method. Performs all startup of
9 * the application.
288
Appendix C Implementations of UML Diagrams
10 */
11 public class Main {
12 public static void main(String[] args) {
13 RegistryCreator creator = new RegistryCreator();
14 Controller contr = new Controller(creator);
15 new View(contr);
16 }
17 }
Listing C.101 Java code implementing the Main class in figure 5.35
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 }
Listing C.102 Java code implementing the CarRegistry class in figure 5.35
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Rental;
4
5 /**
6 * Contains all calls to the data store with performed rentals.
7 */
8 public class RentalRegistry {
9 }
Listing C.103 Java code implementing the RentalRegistry class in figure 5.35
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * This class is responsible for instantiating all registries.
5 */
6 public class RegistryCreator {
7 private CarRegistry carRegistry = new CarRegistry();
8 private RentalRegistry rentalRegistry = new RentalRegistry();
9
10 /**
289
Appendix C Implementations of UML Diagrams
Listing C.104 Java code implementing the RegistryCreator class in figure 5.35
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5
6 /**
7 * This is the application’s only controller class. All calls to
8 * the model pass through here.
9 */
10 public class Controller {
11 /**
12 * Creates a new instance.
13 *
14 * @param regCreator Used to get all classes that handle
15 * database calls.
16 */
17 public Controller(RegistryCreator regCreator) {
18 }
19 }
Listing C.105 Java code implementing the Controller class in figure 5.35
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
290
Appendix C Implementations of UML Diagrams
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
11 /**
12 * Creates a new instance.
13 *
14 * @param contr The controller that is used for all
15 * operations.
16 */
17 public View(Controller contr) {
18 }
19 }
Listing C.106 Java code implementing the View class in figure 5.35
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4
5 /**
6 * This program has no view, instead, this class is a placeholder
7 * for the entire view.
8 */
9 public class View {
10 private Controller contr;
11
12 /**
13 * Creates a new instance.
14 *
15 * @param contr The controller that is used for all
16 * operations.
17 */
18 public View(Controller contr) {
19 }
20 }
Listing C.107 Java code implementing the View class in figure 5.36
291
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.dbhandler.RegistryCreator;
6 import se.kth.ict.oodbook.design.casestudy.dbhandler.RentalRegistry;
7 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
8 import se.kth.ict.oodbook.design.casestudy.model.Rental;
9
10 /**
11 * This is the application’s only controller class. All calls to
12 * the model pass through here.
13 */
14 public class Controller {
15 private CarRegistry carRegistry;
16 private RentalRegistry rentalRegistry;
17 private Rental rental;
18
19 /**
20 * Creates a new instance.
21 *
22 * @param regCreator Used to get all classes that handle
23 * database calls.
24 */
25 public Controller(RegistryCreator regCreator) {
26 }
27
28 /**
29 * Search for a car matching the specified search criteria.
30 *
31 * @param searchedCar This object contains the search criteria.
32 * Fields in the object that are set to
33 * <code>null</code> or
34 * <code>false</code> are ignored.
35 * @return The best match of the search criteria.
36 */
37 public CarDTO searchMatchingCar(CarDTO searchedCar) {
38 }
39
40 /**
41 * Registers a new customer. Only registered customers can
42 * rent cars.
43 *
44 * @param customer The customer that will be registered.
45 */
46 public void registerCustomer(CustomerDTO customer) {
292
Appendix C Implementations of UML Diagrams
47 }
48
49 /**
50 * Books the specified car. After calling this method, the car
51 * can not be booked by any other customer. This method also
52 * permanently saves information about the current rental.
53 *
54 * @param car The car that will be booked.
55 */
56 public void bookCar(CarDTO car) {
57 }
58 }
Listing C.108 Java code implementing the Controller class in figure 5.36
1 package se.kth.ict.oodbook.design.casestudy.startup;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.RegistryCreator;
5 import se.kth.ict.oodbook.design.casestudy.view.View;
6
7 /**
8 * Contains the <code>main</code> method. Performs all startup of
9 * the application.
10 */
11 public class Main {
12 /**
13 * Starts the application.
14 *
15 * @param args The application does not take any command line
16 * parameters.
17 */
18 public static void main(String[] args) {
19 }
20 }
Listing C.109 Java code implementing the Main class in figure 5.36
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5
6 /**
7 * Represents one particular rental transaction, where one
293
Appendix C Implementations of UML Diagrams
Listing C.110 Java code implementing the Rental class in figure 5.36
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * This class is responsible for instantiating all registries.
5 */
6 public class RegistryCreator {
7 /**
8 * Get the value of rentalRegistry
9 *
10 * @return the value of rentalRegistry
11 */
12 public RentalRegistry getRentalRegistry() {
13 }
14
15 /**
16 * Get the value of carRegistry
17 *
18 * @return the value of carRegistry
19 */
294
Appendix C Implementations of UML Diagrams
Listing C.111 Java code implementing the RegistryCreator class in figure 5.36
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 CarRegistry() {
9 }
10
11 /**
12 * Search for a car matching the specified search criteria.
13 *
14 * @param searchedCar This object contains the search criteria.
15 * Fields in the object that are set to
16 * <code>null</code> or <code>false</code>
17 * are ignored.
18 * @return The best match of the search criteria.
19 */
20 public CarDTO findCar(CarDTO searchedCar) {
21 }
22
23 /**
24 * Books the specified car. After calling this method, the car
25 * can not be booked by any other customer.
26 *
27 * @param car The car that will be booked.
28 */
29 public void bookCar(CarDTO car) {
30 }
31 }
Listing C.112 Java code implementing the CarRegistry class in figure 5.36
1 package se.kth.ict.oodbook.design.casestudy.dbhandler;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Rental;
4
5 /**
295
Appendix C Implementations of UML Diagrams
Listing C.113 Java code implementing the RentalRegistry class in figure 5.36
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.model.Amount;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
11 private Controller contr;
12
13 // Somewhere in the code. The used amount (100) is not
14 // specified in the diagram.
15 Amount paidAmount = new Amount(100);
16 contr.pay(paidAmount);
17 }
Listing C.114 Java code implementing the View class in figure 5.37
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Amount;
4 import se.kth.ict.oodbook.design.casestudy.model.Rental;
296
Appendix C Implementations of UML Diagrams
5
6 /**
7 * This is the application’s only controller class. All
8 * calls to the model pass through here.
9 */
10 public class Controller {
11 /**
12 * Handles rental payment. Updates the balance of
13 * the cash register where the payment was
14 * performed. Calculates change. Prints the receipt.
15 *
16 * @param amount The paid amount.
17 */
18 public void pay(Amount paidAmt) {
19 }
20 }
Listing C.115 Java code implementing the Controller class in figure 5.37
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents an amount of money
5 */
6 public final class Amount {
7 private final int amount;
8
9 public Amount(int amount) {
10 this.amount = amount;
11 }
12 }
Listing C.116 Java code implementing the Amount class in figure 5.37
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.model.Amount;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
297
Appendix C Implementations of UML Diagrams
Listing C.117 Java code implementing the View class in figure 5.38
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Amount;
4 import se.kth.ict.oodbook.design.casestudy.model.Rental;
5
6 /**
7 * This is the application’s only controller class. All
8 * calls to the model pass through here.
9 */
10 public class Controller {
11 /**
12 * Handles rental payment. Updates the balance of
13 * the cash register where the payment was
14 * performed. Calculates change. Prints the receipt.
15 *
16 * @param amount The paid amount.
17 */
18 public void pay(Amount paidAmt) {
19 CashPayment payment = new CashPayment(paidAmt);
20 rental.pay(payment);
21 cashRegister.addPayment(payment);
22 }
23 }
Listing C.118 Java code implementing the Controller class in figure 5.38
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a cash register. There shall be one instance of
5 * this class for each register.
6 */
298
Appendix C Implementations of UML Diagrams
Listing C.119 Java code implementing the CashRegister class in figure 5.38
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents one specific payment for one specific rental. The
5 * rental is payed with cash.
6 */
7 public class CashPayment {
8 private Amount paidAmt;
9
10 /**
11 * Creates a new instance. The customer handed over the
12 * specified amount.
13 *
14 * @param paidAmt The amount of cash that was handed over
15 * by the customer.
16 */
17 public CashPayment(Amount paidAmt) {
18 this.paidAmt = paidAmt;
19 }
20
21 /**
22 * Calculates the total cost of the specified rental.
23 *
24 * @param paidRental The rental for which the customer is
25 * paying.
26 */
27 void calculateTotalCost(Rental paidRental) {
28 }
29 }
Listing C.120 Java code implementing the CashPayment class in figure 5.38
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.dbhandler.CarRegistry;
5
6 /**
299
Appendix C Implementations of UML Diagrams
Listing C.121 Java code implementing the Rental class in figure 5.38
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents an amount of money
5 */
6 public final class Amount {
7 private final int amount;
8
9 public Amount(int amount) {
10 this.amount = amount;
11 }
12 }
Listing C.122 Java code implementing the Amount class in figure 5.38
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.model.Amount;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
300
Appendix C Implementations of UML Diagrams
Listing C.123 Java code implementing the View class in figure 5.39
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Amount;
4 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
5 import se.kth.ict.oodbook.design.casestudy.model.CashPayment;
6 import se.kth.ict.oodbook.design.casestudy.model.CashRegister;
7 import se.kth.ict.oodbook.design.casestudy.model.Rental;
8 import se.kth.ict.oodbook.design.casestudy.model.Receipt;
9
10 /**
11 * This is the application’s only controller class. All
12 * calls to the model pass through here.
13 */
14 public class Controller {
15 private Rental rental;
16 private CashRegister cashRegister;
17 private Printer printer;
18
19 /**
20 * Handles rental payment. Updates the balance of
21 * the cash register where the payment was
22 * performed. Calculates change. Prints the receipt.
23 *
24 * @param amount The paid amount.
25 */
26 public void pay(Amount amount) {
27 CashPayment payment = new CashPayment(paidAmt);
28 rental.pay(payment);
29 cashRegister.addPayment(payment);
30 Receipt receipt = rental.getReceipt();
31 printer.printReceipt(receipt);
32 }
33 }
Listing C.124 Java code implementing the Controller class in figure 5.39
301
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a cash register. There shall be one instance of
5 * this class for each register.
6 */
7 public class CashRegister {
8 public void addPayment(CashPayment payment) {
9 }
10 }
Listing C.125 Java code implementing the CashRegister class in figure 5.39
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents one specific payment for one specific rental. The
5 * rental is payed with cash.
6 */
7 public class CashPayment {
8 private Amount paidAmt;
9
10 /**
11 * Creates a new instance. The customer handed over the
12 * specified amount.
13 *
14 * @param paidAmt The amount of cash that was handed over
15 * by the customer.
16 */
17 public CashPayment(Amount paidAmt) {
18 this.paidAmt = paidAmt;
19 }
20
21 /**
22 * Calculates the total cost of the specified rental.
23 *
24 * @param paidRental The rental for which the customer is
25 * paying.
26 */
27 void calculateTotalCost(Rental paidRental) {
28 }
29 }
Listing C.126 Java code implementing the CashPayment class in figure 5.39
302
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents one particular rental transaction, where one
5 * particular car is rented by one particular customer.
6 */
7 public class Rental {
8 /**
9 * This rental is paid using the specified payment.
10 *
11 * @param payment The payment used to pay this rental.
12 */
13 public void pay(CashPayment payment) {
14 payment.calculateTotalCost(this);
15 }
16
17 /**
18 * Returns a receipt for the current rental.
19 */
20 public Receipt getReceipt() {
21 return new Receipt(this);
22 }
23 }
Listing C.127 Java code implementing the Rental class in figure 5.39
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * The receipt of a rental
5 */
6 public class Receipt {
7
8 /**
9 * Creates a new instance.
10 *
11 * @param rental The rental proved by this receipt.
12 */
13 Receipt(Rental rental) {
14 }
15
16 }
Listing C.128 Java code implementing the Receipt class in figure 5.39
303
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Receipt;
4
5 /**
6 * The interface to the printer, used for all printouts initiated
7 * by this program.
8 */
9 public class Printer {
10 public void printReceipt(Receipt receipt) {
11
12 }
13 }
Listing C.129 Java code implementing the Printer class in figure 5.39
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents an amount of money
5 */
6 public final class Amount {
7 private final int amount;
8
9 public Amount(int amount) {
10 this.amount = amount;
11 }
12 }
Listing C.130 Java code implementing the Amount class in figure 5.39
1 package se.kth.ict.oodbook.design.casestudy.startup;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
5 import se.kth.ict.oodbook.design.casestudy.integration.RegistryCreator;
6 import se.kth.ict.oodbook.design.casestudy.view.View;
7
8 /**
9 * Contains the <code>main</code> method. Performs all startup of the
304
Appendix C Implementations of UML Diagrams
10 * application.
11 */
12 public class Main {
13 /**
14 * Starts the application.
15 *
16 * @param args The application does not take any command line
17 * parameters.
18 */
19 public static void main(String[] args) {
20 RegistryCreator creator = new RegistryCreator();
21 Printer printer = new Printer();
22 Controller contr = new Controller(creator, printer);
23 new View(contr).sampleExecution();
24 }
25 }
Listing C.131 Java code implementing the Main class in figure 5.40
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 }
Listing C.132 Java code implementing the CarRegistry class in figure 5.40
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Rental;
4
5 /**
6 * Contains all calls to the data store with performed rentals.
7 */
8 public class RentalRegistry {
9 }
Listing C.133 Java code implementing the RentalRegistry class in figure 5.40
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
305
Appendix C Implementations of UML Diagrams
3 /**
4 * This class is responsible for instantiating all registries.
5 */
6 public class RegistryCreator {
7 private CarRegistry carRegistry = new CarRegistry();
8 private RentalRegistry rentalRegistry = new RentalRegistry();
9
10 /**
11 * Get the value of rentalRegistry
12 *
13 * @return the value of rentalRegistry
14 */
15 public RentalRegistry getRentalRegistry() {
16 return rentalRegistry;
17 }
18
19 /**
20 * Get the value of carRegistry
21 *
22 * @return the value of carRegistry
23 */
24 public CarRegistry getCarRegistry() {
25 return carRegistry;
26 }
27 }
Listing C.134 Java code implementing the RegistryCreator class in figure 5.40
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 /**
4 * The interface to the printer, used for all printouts
5 * initiated by this program.
6 */
7 public class Printer {
8 }
Listing C.135 Java code implementing the Printer class in figure 5.40
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a cash register. There shall be one
5 * instance of this class for each register.
6 */
306
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.integration.CarRegistry;
4 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
5 import se.kth.ict.oodbook.design.casestudy.integration.RegistryCreator;
6 import se.kth.ict.oodbook.design.casestudy.integration.RentalRegistry;
7 import se.kth.ict.oodbook.design.casestudy.model.CashRegister;
8
9 /**
10 * This is the application’s only controller class. All calls to the
11 * model pass through here.
12 */
13 public class Controller {
14 private CarRegistry carRegistry;
15 private RentalRegistry rentalRegistry;
16 private CashRegister cashRegister;
17 private Printer printer;
18
19 /**
20 * Creates a new instance.
21 *
22 * @param regCreator Used to get all classes that handle database
23 * calls.
24 * @param printer Interface to printer.
25 */
26 public Controller(RegistryCreator regCreator, Printer printer) {
27 this.carRegistry = regCreator.getCarRegistry();
28 this.rentalRegistry = regCreator.getRentalRegistry();
29 this.printer = printer;
30 this.cashRegister = new CashRegister();
31 }
32 }
Listing C.137 Java code implementing the Controller class in figure 5.40
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4
5 /**
307
Appendix C Implementations of UML Diagrams
Listing C.138 Java code implementing the View class in figure 5.40
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4
5 /**
6 * This program has no view, instead, this class is a placeholder
7 * for the entire view.
8 */
9 public class View {
10 private Controller contr;
11
12 /**
13 * Creates a new instance.
14 *
15 * @param contr The controller that is used for all
16 * operations.
17 */
18 public View(Controller contr) {
19 }
20 }
Listing C.139 Java code implementing the View class in figure 5.41
308
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Amount;
4 import se.kth.ict.oodbook.design.casestudy.integration.CarDTO;
5 import se.kth.ict.oodbook.design.casestudy.integration.CarRegistry;
6 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
7 import se.kth.ict.oodbook.design.casestudy.integration.RegistryCreator;
8 import se.kth.ict.oodbook.design.casestudy.integration.RentalRegistry;
9 import se.kth.ict.oodbook.design.casestudy.model.CashPayment;
10 import se.kth.ict.oodbook.design.casestudy.model.CashRegister;
11 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
12 import se.kth.ict.oodbook.design.casestudy.model.Rental;
13 import se.kth.ict.oodbook.design.casestudy.model.Receipt;
14
15 /**
16 * This is the application’s only controller class. All calls to
17 * the model pass through here.
18 */
19 public class Controller {
20 private CarRegistry carRegistry;
21 private RentalRegistry rentalRegistry;
22 private Rental rental;
23
24 /**
25 * Creates a new instance.
26 *
27 * @param regCreator Used to get all classes that handle
28 * database calls.
29 */
30 public Controller(RegistryCreator regCreator) {
31 }
32
33 /**
34 * Search for a car matching the specified search criteria.
35 *
36 * @param searchedCar This object contains the search criteria.
37 * Fields in the object that are set to
38 * <code>null</code> or
39 * <code>false</code> are ignored.
40 * @return The best match of the search criteria.
41 */
42 public CarDTO searchMatchingCar(CarDTO searchedCar) {
43 }
44
45 /**
46 * Registers a new customer. Only registered customers can
309
Appendix C Implementations of UML Diagrams
47 * rent cars.
48 *
49 * @param customer The customer that will be registered.
50 */
51 public void registerCustomer(CustomerDTO customer) {
52 }
53
54 /**
55 * Books the specified car. After calling this method, the car
56 * can not be booked by any other customer. This method also
57 * permanently saves information about the current rental.
58 *
59 * @param car The car that will be booked.
60 */
61 public void bookCar(CarDTO car) {
62 }
63
64 /**
65 * Handles rental payment. Updates the balance of the cash register
66 * where the payment was performed. Calculates change. Prints the
67 * receipt.
68 *
69 * @param paidAmt The paid amount.
70 */
71 public void pay(Amount paidAmt) {
72 }
73 }
Listing C.140 Java code implementing the Controller class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.startup;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
5 import se.kth.ict.oodbook.design.casestudy.integration.RegistryCreator;
6 import se.kth.ict.oodbook.design.casestudy.view.View;
7
8 /**
9 * Contains the <code>main</code> method. Performs all startup of
10 * the application.
11 */
12 public class Main {
13 /**
14 * Starts the application.
15 *
16 * @param args The application does not take any command line
310
Appendix C Implementations of UML Diagrams
17 * parameters.
18 */
19 public static void main(String[] args) {
20 }
21 }
Listing C.141 Java code implementing the Main class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 import se.kth.ict.oodbook.design.casestudy.integration.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.integration.CarRegistry;
5
6 /**
7 * Represents one particular rental transaction, where one
8 * particular car is rented by one particular customer.
9 */
10 public class Rental {
11 private CarRegistry carRegistry;
12
13 /**
14 * Creates a new instance, representing a rental made by the
15 * specified customer.
16 *
17 * @param customer The renting customer.
18 * @param carRegistry The data store with information about
19 * available cars.
20 */
21 public Rental(CustomerDTO customer, CarRegistry carRegistry) {
22 }
23
24 /**
25 * Specifies the car that was rented.
26 *
27 * @param rentedCar The car that was rented.
28 */
29 public void setRentedCar(CarDTO rentedCar) {
30 }
31
32 /**
33 * This rental is paid using the specified payment.
34 *
35 * @param payment The payment used to pay this rental.
36 */
37 public void pay(CashPayment payment) {
38 }
311
Appendix C Implementations of UML Diagrams
39
40 /**
41 * Returns a receipt for the current rental.
42 */
43 public Receipt getReceipt() {
44 }
45 }
Listing C.142 Java code implementing the Rental class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * The receipt of a rental
5 */
6 public class Receipt {
7 /**
8 * Creates a new instance.
9 *
10 * @param rental The rental proved by this receipt.
11 */
12 Receipt(Rental rental) {
13 }
14 }
Listing C.143 Java code implementing the Receipt class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents one specific payment for one specific rental.
5 * The rental is payed with cash.
6 */
7 public class CashPayment {
8 /**
9 * Creates a new instance. The customer handed over
10 * the specified amount.
11 *
12 * @param paidAmt The amount of cash that was handed
13 * over by the customer.
14 */
15 public CashPayment(Amount paidAmt) {
16 }
17
18 /**
312
Appendix C Implementations of UML Diagrams
Listing C.144 Java code implementing the CashPayment class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a cash register. There shall be one instance
5 * of this class for each register.
6 */
7 public class CashRegister {
8 public void addPayment(CashPayment payment) {
9 }
10 }
Listing C.145 Java code implementing the CashRegister class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Receipt;
4
5 /**
6 * The interface to the printer, used for all printouts
7 * initiated by this program.
8 */
9 public class Printer {
10 public void printReceipt(Receipt receipt) {
11 }
12 }
Listing C.146 Java code implementing the Printer class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 /**
4 * This class is responsible for instantiating all registries.
5 */
313
Appendix C Implementations of UML Diagrams
Listing C.147 Java code implementing the RegistryCreator class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 CarRegistry() {
9 }
10
11 /**
12 * Search for a car matching the specified search criteria.
13 *
14 * @param searchedCar This object contains the search criteria.
15 * Fields in the object that are set to
16 * <code>null</code> or <code>false</code>
17 * are ignored.
18 * @return The best match of the search criteria.
19 */
20 public CarDTO findCar(CarDTO searchedCar) {
21 }
22
23 /**
24 * Books the specified car. After calling this method, the car
25 * can not be booked by any other customer.
26 *
314
Appendix C Implementations of UML Diagrams
Listing C.148 Java code implementing the CarRegistry class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Rental;
4
5 /**
6 * Contains all calls to the data store with performed rentals.
7 */
8 public class RentalRegistry {
9 RentalRegistry() {
10 }
11
12 /**
13 * Saves the specified rental permanently.
14 *
15 * @param rental The rental that will be saved.
16 */
17 public void saveRental(Rental rental) {
18 }
19 }
Listing C.149 Java code implementing the RentalRegistry class in figure 5.41
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.model.Amount;
5
6 /**
7 * This program has no view, instead, this class is a placeholder
8 * for the entire view.
9 */
10 public class View {
11 private Controller contr;
315
Appendix C Implementations of UML Diagrams
12
13 // Somewhere in the code. The used amount (100) is not
14 // specified in the diagram.
15 Amount paidAmount = new Amount(100);
16 contr.pay(paidAmount);
17 }
Listing C.150 Java code implementing the View class in figure 5.42
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Amount;
4 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
5 import se.kth.ict.oodbook.design.casestudy.model.CashPayment;
6 import se.kth.ict.oodbook.design.casestudy.model.CashRegister;
7 import se.kth.ict.oodbook.design.casestudy.model.Rental;
8
9 /**
10 * This is the application’s only controller class. All
11 * calls to the model pass through here.
12 */
13 public class Controller {
14 private Rental rental;
15 private CashRegister cashRegister;
16 private Printer printer;
17
18 /**
19 * Handles rental payment. Updates the balance of the cash
20 * register where the payment was performed. Calculates
21 * change. Prints the receipt.
22 *
23 * @param paidAmt The paid amount.
24 */
25 public void pay(Amount paidAmt) {
26 CashPayment payment = new CashPayment(paidAmt);
27 rental.pay(payment);
28 cashRegister.addPayment(payment);
29 rental.printReceipt(printer);
30 }
31 }
Listing C.151 Java code implementing the Controller class in figure 5.42
1 package se.kth.ict.oodbook.design.casestudy.model;
2
316
Appendix C Implementations of UML Diagrams
3 /**
4 * Represents a cash register. There shall be one instance of
5 * this class for each register.
6 */
7 public class CashRegister {
8 public void addPayment(CashPayment payment) {
9 }
10 }
Listing C.152 Java code implementing the CashRegister class in figure 5.42
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents one specific payment for one specific rental. The
5 * rental is payed with cash.
6 */
7 public class CashPayment {
8 private Amount paidAmt;
9
10 /**
11 * Creates a new instance. The customer handed over the
12 * specified amount.
13 *
14 * @param paidAmt The amount of cash that was handed over
15 * by the customer.
16 */
17 public CashPayment(Amount paidAmt) {
18 this.paidAmt = paidAmt;
19 }
20
21 /**
22 * Calculates the total cost of the specified rental.
23 *
24 * @param paidRental The rental for which the customer is
25 * paying.
26 */
27 void calculateTotalCost(Rental paidRental) {
28 }
29 }
Listing C.153 Java code implementing the CashPayment class in figure 5.42
1 package se.kth.ict.oodbook.design.casestudy.model;
2
317
Appendix C Implementations of UML Diagrams
3 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
4
5 /**
6 * Represents one particular rental transaction, where one
7 * particular car is rented by one particular customer.
8 */
9 public class Rental {
10 /**
11 * This rental is paid using the specified payment.
12 *
13 * @param payment The payment used to pay this rental.
14 */
15 public void pay(CashPayment payment) {
16 payment.calculateTotalCost(this);
17 }
18
19 /**
20 * Prints a receipt for the current rental on the
21 * specified printer.
22 */
23 public void printReceipt(Printer printer) {
24 Receipt receipt = new Receipt(this);
25 printer.printReceipt(receipt);
26 }
27 }
Listing C.154 Java code implementing the Rental class in figure 5.42
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * The receipt of a rental
5 */
6 public class Receipt {
7
8 /**
9 * Creates a new instance.
10 *
11 * @param rental The rental proved by this receipt.
12 */
13 Receipt(Rental rental) {
14 }
15
16 }
Listing C.155 Java code implementing the Receipt class in figure 5.42
318
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Receipt;
4
5 /**
6 * The interface to the printer, used for all printouts initiated
7 * by this program.
8 */
9 public class Printer {
10 public void printReceipt(Receipt receipt) {
11
12 }
13 }
Listing C.156 Java code implementing the Printer class in figure 5.42
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents an amount of money
5 */
6 public final class Amount {
7 private final int amount;
8
9 public Amount(int amount) {
10 this.amount = amount;
11 }
12 }
Listing C.157 Java code implementing the Amount class in figure 5.42
1 package se.kth.ict.oodbook.design.casestudy.view;
2
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4
5 /**
6 * This program has no view, instead, this class is a placeholder
7 * for the entire view.
8 */
9 public class View {
319
Appendix C Implementations of UML Diagrams
Listing C.158 Java code implementing the View class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.controller;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Amount;
4 import se.kth.ict.oodbook.design.casestudy.integration.CarDTO;
5 import se.kth.ict.oodbook.design.casestudy.integration.CarRegistry;
6 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
7 import se.kth.ict.oodbook.design.casestudy.integration.RegistryCreator;
8 import se.kth.ict.oodbook.design.casestudy.integration.RentalRegistry;
9 import se.kth.ict.oodbook.design.casestudy.model.CashPayment;
10 import se.kth.ict.oodbook.design.casestudy.model.CashRegister;
11 import se.kth.ict.oodbook.design.casestudy.model.CustomerDTO;
12 import se.kth.ict.oodbook.design.casestudy.model.Rental;
13 import se.kth.ict.oodbook.design.casestudy.model.Receipt;
14
15 /**
16 * This is the application’s only controller class. All calls to
17 * the model pass through here.
18 */
19 public class Controller {
20 private CarRegistry carRegistry;
21 private RentalRegistry rentalRegistry;
22 private Rental rental;
23
24 /**
25 * Creates a new instance.
26 *
27 * @param regCreator Used to get all classes that handle
28 * database calls.
29 */
30 public Controller(RegistryCreator regCreator) {
31 }
32
320
Appendix C Implementations of UML Diagrams
33 /**
34 * Search for a car matching the specified search criteria.
35 *
36 * @param searchedCar This object contains the search criteria.
37 * Fields in the object that are set to
38 * <code>null</code> or
39 * <code>false</code> are ignored.
40 * @return The best match of the search criteria.
41 */
42 public CarDTO searchMatchingCar(CarDTO searchedCar) {
43 }
44
45 /**
46 * Registers a new customer. Only registered customers can
47 * rent cars.
48 *
49 * @param customer The customer that will be registered.
50 */
51 public void registerCustomer(CustomerDTO customer) {
52 }
53
54 /**
55 * Books the specified car. After calling this method, the car
56 * can not be booked by any other customer. This method also
57 * permanently saves information about the current rental.
58 *
59 * @param car The car that will be booked.
60 */
61 public void bookCar(CarDTO car) {
62 }
63
64 /**
65 * Handles rental payment. Updates the balance of the cash register
66 * where the payment was performed. Calculates change. Prints the
67 * receipt.
68 *
69 * @param paidAmt The paid amount.
70 */
71 public void pay(Amount paidAmt) {
72 }
73 }
Listing C.159 Java code implementing the Controller class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.startup;
2
321
Appendix C Implementations of UML Diagrams
3 import se.kth.ict.oodbook.design.casestudy.controller.Controller;
4 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
5 import se.kth.ict.oodbook.design.casestudy.integration.RegistryCreator;
6 import se.kth.ict.oodbook.design.casestudy.view.View;
7
8 /**
9 * Contains the <code>main</code> method. Performs all startup of
10 * the application.
11 */
12 public class Main {
13 /**
14 * Starts the application.
15 *
16 * @param args The application does not take any command line
17 * parameters.
18 */
19 public static void main(String[] args) {
20 }
21 }
Listing C.160 Java code implementing the Main class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 import se.kth.ict.oodbook.design.casestudy.integration.CarDTO;
4 import se.kth.ict.oodbook.design.casestudy.integration.CarRegistry;
5 import se.kth.ict.oodbook.design.casestudy.integration.Printer;
6
7 /**
8 * Represents one particular rental transaction, where one
9 * particular car is rented by one particular customer.
10 */
11 public class Rental {
12 private CarRegistry carRegistry;
13
14 /**
15 * Creates a new instance, representing a rental made by the
16 * specified customer.
17 *
18 * @param customer The renting customer.
19 * @param carRegistry The data store with information about
20 * available cars.
21 */
22 public Rental(CustomerDTO customer, CarRegistry carRegistry) {
23 }
24
322
Appendix C Implementations of UML Diagrams
25 /**
26 * Specifies the car that was rented.
27 *
28 * @param rentedCar The car that was rented.
29 */
30 public void setRentedCar(CarDTO rentedCar) {
31 }
32
33 /**
34 * This rental is paid using the specified payment.
35 *
36 * @param payment The payment used to pay this rental.
37 */
38 public void pay(CashPayment payment) {
39 }
40
41 /**
42 * Prints a receipt for the current rental on the specified
43 * printer.
44 */
45 public void printReceipt(Printer printer) {
46 }
47 }
Listing C.161 Java code implementing the Rental class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * The receipt of a rental
5 */
6 public class Receipt {
7 /**
8 * Creates a new instance.
9 *
10 * @param rental The rental proved by this receipt.
11 */
12 Receipt(Rental rental) {
13 }
14 }
Listing C.162 Java code implementing the Receipt class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.model;
2
323
Appendix C Implementations of UML Diagrams
3 /**
4 * Represents one specific payment for one specific rental.
5 * The rental is payed with cash.
6 */
7 public class CashPayment {
8 /**
9 * Creates a new instance. The customer handed over
10 * the specified amount.
11 *
12 * @param paidAmt The amount of cash that was handed
13 * over by the customer.
14 */
15 public CashPayment(Amount paidAmt) {
16 }
17
18 /**
19 * Calculates the total cost of the specified rental.
20 *
21 * @param paidRental The rental for which the customer
22 * is paying.
23 */
24 void calculateTotalCost(Rental paidRental) {
25 }
26 }
Listing C.163 Java code implementing the CashPayment class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.model;
2
3 /**
4 * Represents a cash register. There shall be one instance
5 * of this class for each register.
6 */
7 public class CashRegister {
8 public void addPayment(CashPayment payment) {
9 }
10 }
Listing C.164 Java code implementing the CashRegister class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Receipt;
4
5 /**
324
Appendix C Implementations of UML Diagrams
Listing C.165 Java code implementing the Printer class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 /**
4 * This class is responsible for instantiating all registries.
5 */
6 public class RegistryCreator {
7 /**
8 * Get the value of rentalRegistry
9 *
10 * @return the value of rentalRegistry
11 */
12 public RentalRegistry getRentalRegistry() {
13 }
14
15 /**
16 * Get the value of carRegistry
17 *
18 * @return the value of carRegistry
19 */
20 public CarRegistry getCarRegistry() {
21 }
22 }
Listing C.166 Java code implementing the RegistryCreator class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 /**
4 * Contains all calls to the data store with cars that may be
5 * rented.
6 */
7 public class CarRegistry {
8 CarRegistry() {
9 }
10
325
Appendix C Implementations of UML Diagrams
11 /**
12 * Search for a car matching the specified search criteria.
13 *
14 * @param searchedCar This object contains the search criteria.
15 * Fields in the object that are set to
16 * <code>null</code> or <code>false</code>
17 * are ignored.
18 * @return The best match of the search criteria.
19 */
20 public CarDTO findCar(CarDTO searchedCar) {
21 }
22
23 /**
24 * Books the specified car. After calling this method, the car
25 * can not be booked by any other customer.
26 *
27 * @param car The car that will be booked.
28 */
29 public void bookCar(CarDTO car) {
30 }
31 }
Listing C.167 Java code implementing the CarRegistry class in figure 5.43
1 package se.kth.ict.oodbook.design.casestudy.integration;
2
3 import se.kth.ict.oodbook.design.casestudy.model.Rental;
4
5 /**
6 * Contains all calls to the data store with performed rentals.
7 */
8 public class RentalRegistry {
9 RentalRegistry() {
10 }
11
12 /**
13 * Saves the specified rental permanently.
14 *
15 * @param rental The rental that will be saved.
16 */
17 public void saveRental(Rental rental) {
18 }
19 }
Listing C.168 Java code implementing the RentalRegistry class in figure 5.43
326
Appendix C Implementations of UML Diagrams
1 package se.kth.ict.oodbook.exception.uml;
2
3 public class MyClass {
4 public void myMethod() throws MyException, AnotherException {
5 }
6 }
Listing C.169 Java code implementing the MyClass class in figure 8.2a
1 package se.kth.ict.oodbook.exception.uml;
2
3 public class MyException extends Exception {
4 }
1 package se.kth.ict.oodbook.exception.uml;
2
3 public class AnotherException extends Exception {
4 }
1 package se.kth.ict.oodbook.exception.uml;
2
3 public class MyClass {
4 // The diagram shows that one or more of the methods in
5 // this class throws MyException, but it does not show
6 // which method(s).
7 public void myMethod() {
8 }
9
10 public void anotherMethod() {
11 }
12 }
Listing C.172 Java code implementing the MyClass class in figure 8.2b
1 package se.kth.ict.oodbook.exception.uml;
327
Appendix C Implementations of UML Diagrams
2
3 public class MyException extends Exception {
4 }
1 package se.kth.ict.oodbook.exception.uml;
2
3 public class MyClass {
4 public void myMethod() throws MyException {
5 }
6 }
Listing C.174 Java code implementing the MyClass class in figure 8.3
1 package se.kth.ict.oodbook.exception.uml;
2
3 public class MyException extends Exception {
4 }
1 package se.kth.ict.oodbook.exception.uml;
2
3 public class SomeClass {
4 private MyClass myClass;
5
6 // Somewhere, in some method in this class:
7 try {
8 myClass.myMethod();
9 } catch (MyException exception) {
10
11 }
12 }
Listing C.176 Java code implementing the SomeClass class in figure 8.3.
Strictly speaking, the UML diagram does not tell whether there is a try-catch
block or not.
328
Appendix C Implementations of UML Diagrams
1 package se.leiflindback.oodbook.polymorphism.uml;
2
3 interface InterfaceA {
4 public void methodA();
5 }
1 package se.leiflindback.oodbook.polymorphism.uml;
2
3 public class ClassA implements InterfaceA {
4 @Override
5 public void methodA() {
6 }
7 }
Listing C.178 Java code implementing the ClassA class in figure 9.1a
1 package se.leiflindback.oodbook.polymorphism.uml;
2
3 public class ClassB extends ClassC{
4 }
Listing C.179 Java code implementing the ClassB class in figure 9.1b
1 package se.leiflindback.oodbook.polymorphism.uml;
2
3 public class ClassC {
4 }
Listing C.180 Java code implementing the ClassC class in figure 9.1b
1 package se.leiflindback.oodbook.polymorphism.uml.seqClass;
2
329
Appendix C Implementations of UML Diagrams
Listing C.181 Java code implementing the AnyClass object in figure 9.2a
1 package se.leiflindback.oodbook.polymorphism.uml.seqClass;
2
3 public class ClassA {
4 public void methodA() {
5 }
6 }
Listing C.182 Java code implementing the ClassA object in figure 9.2a
1 package se.leiflindback.oodbook.polymorphism.uml.seqInterf;
2
3 public class AnyClass {
4 private InterfaceA callee;
5
6 //Somewhere in some method.
7 callee.methodA();
8 }
Listing C.183 Java code implementing the AnyClass object in figure 9.2b
1 package se.leiflindback.oodbook.polymorphism.uml.seqInterf;
2
3 interface InterfaceA {
4 public void methodA();
5 }
Listing C.184 Java code implementing the InterfaceA object in figure 9.2b
1 package se.leiflindback.oodbook.polymorphism.uml.seqInterfClass;
2
3 public class AnyClass {
4 private InterfaceA callee;
5
6 //Somewhere in some method.
7 callee.methodA();
330
Appendix C Implementations of UML Diagrams
8 }
Listing C.185 Java code implementing the AnyClass object in figure 9.2c
1 package se.leiflindback.oodbook.polymorphism.uml.seqInterfClass;
2
3 public class SomeUnknownClass implements InterfaceA {
4 private ClassA callee;
5
6 @Override
7 public void methodA() {
8 callee.methodA();
9 }
10 }
Listing C.186 Java code implementing the InterfaceA object in figure 9.2c
1 package se.leiflindback.oodbook.polymorphism.uml.seqInterfClass;
2
3 public class ClassA {
4 public void methodA() {
5 }
6 }
Listing C.187 Java code implementing the ClassA object in figure 9.2c
1 package package1;
2
3 public class Class1 {
4 protected void protectedMethod() {
5
6 }
7 }
331
Appendix C Implementations of UML Diagrams
1 package package1;
2
3 public class Class2 extends Class1 {
4
5 }
1 package package1;
2
3 public class Class1 {
4 protected void protectedMethod() {
5
6 }
7 }
1 package package1;
2
3 public class Class2 {
4 private Class1 class1;
5 }
1 package package1;
2
3 public class Class1 {
4 protected void protectedMethod() {
5
6 }
7 }
1 package package2;
332
Appendix C Implementations of UML Diagrams
2
3 import package1.Class1;
4
5 public class Class2 extends Class1 {
6
7 }
1 package package1;
2
3 public class Class1 {
4 protected void protectedMethod() {
5
6 }
7 }
1 package package2;
2
3 import package1.Class1;
4
5 public class Class2 {
6 private Class1 class1;
7 }
333
Appendix C Implementations of UML Diagrams
334
Appendix C Implementations of UML Diagrams
14
15 public class Walker extends Animal {
16 public void walk() {
17 }
18 }
19
20 public class Parrot extends Flyer {
21 }
22
23 public class Crow extends Flyer {
24 }
25
26 public class Salmon extends Swimmer {
27 }
28
29 public class Perch extends Swimmer {
30 }
31
32 public class Penguin extends Swimmer {
33 }
34
35 public class Cat extends Walker {
36 }
37
38 public class Dog extends Walker {
39 }
335
Appendix C Implementations of UML Diagrams
14
15 public class Walker {
16 public void walk() {
17 }
18 }
19
20 public class Parrot {
21 private Animal animal;
22 private Flyer flyer;
23
24 public void fly() {
25 flyer.fly();
26 }
27 }
28
29 public class Crow {
30 private Animal animal;
31 private Flyer flyer;
32
33 public void fly() {
34 flyer.fly();
35 }
36 }
37
38 public class Salmon {
39 private Animal animal;
40 private Swimmer swimmer;
41
42 public void swim() {
43 swimmer.swim();
44 }
45 }
46
47 public class Perch {
48 private Animal animal;
49 private Swimmer swimmer;
50
51 public void swim() {
52 swimmer.swim();
53 }
54 }
55
56 public class Ostrich {
57 private Animal animal;
58 private Walker walker;
59
336
Appendix C Implementations of UML Diagrams
1 package se.leiflindback.oodbook.despat.observer;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7 * The observed class in a general implementation of the
8 * observer pattern.
9 */
10 public class ObservedClass {
11 private List<Observer> observers = new ArrayList<>();
12
13 /**
14 * Registers observers. Any <code>Observer</code> that is
15 * passed to this method will be notified when this object
16 * changes state.
17 *
18 * @param observer The observer that shall be registered.
19 */
20 public void addObserver(Observer observer) {
337
Appendix C Implementations of UML Diagrams
21 observers.add(observer);
22 }
23
24 // Called by any method in this class that has changed the
25 // class’ state.
26 private void notifyObservers() {
27 for (Observer observer : observers) {
28 observer.stateHasChanged();
29 }
30 }
31 }
Listing C.199 Java code implementing ObservedClass in figure 9.12. The method
notifyObservers is not shown in the diagram.
1 package se.leiflindback.oodbook.despat.observer;
2
3 /**
4 * The observer interface in a general implementation of the
5 * observer pattern.
6 */
7 public interface Observer {
8 /**
9 * Called when the observed class changes state.
10 */
11 void stateHasChanged();
12 }
1 package se.leiflindback.oodbook.despat.observer;
2
3 /**
4 * The observing class in a general implementation of the
5 * observer pattern.
6 */
7 public class AnyClassThatImplementsObserver implements
8 Observer {
9 @Override
10 public void stateHasChanged() {
11 }
12 }
338
Appendix C Implementations of UML Diagrams
1 package se.leiflindback.oodbook.polymInherit.strategy;
2
3 /**
4 * A class that uses a concrete strategy.
5 */
6 public class Client {
7 private StrategyDefinition strategy;
8 }
1 package se.leiflindback.oodbook.polymInherit.strategy;
2
3 /**
4 * A general example of definition of a strategy.
5 */
6 public interface StrategyDefinition {
7 /**
8 * The work performed by this strategy.
9 */
10 public void algorithm();
11 }
1 package se.leiflindback.oodbook.polymInherit.strategy;
2
3 /**
4 * A concrete implementation of a strategy.
5 */
6 public class ConcreteStrategyA implements StrategyDefinition {
7 @Override
8 public void algorithm() {
9 }
10 }
1 package se.leiflindback.oodbook.polymInherit.strategy;
2
339
Appendix C Implementations of UML Diagrams
3 /**
4 * A concrete implementation of a strategy.
5 */
6 public class ConcreteStrategyB implements StrategyDefinition {
7 @Override
8 public void algorithm() {
9 }
10 }
1 package se.leiflindback.oodbook.polymInherit.factory;
2
3 /**
4 * A class that uses a concrete strategy.
5 */
6 public class Client {
7 private Product product = new Factory().createProduct();
8 }
1 package se.leiflindback.oodbook.polymInherit.factory;
2
3 /**
4 * A generic example of a definition of products created by
5 * a factory.
6 */
7 public interface Product {
8 }
1 package se.leiflindback.oodbook.polymInherit.factory;
2
3 /**
4 * An example of a concrete product
5 */
6 public class ConcreteProductA implements Product {
340
Appendix C Implementations of UML Diagrams
7 }
1 package se.leiflindback.oodbook.polymInherit.factory;
2
3 /**
4 * An example of a concrete product
5 */
6 public class ConcreteProductB implements Product {
7 }
1 package se.leiflindback.oodbook.polymInherit.factory;
2
3 /**
4 * A generic example of a factory.
5 */
6 public class Factory {
7
8 /**
9 * Creates a new product.
10 * @return The newly created product.
11 */
12 public Product createProduct() {
13 return new ConcreteProductA();
14 }
15 }
1 package se.leiflindback.oodbook.polyminherit.composite;
2
3 /**
4 * The client of the algorithm.
5 */
6 public class Client {
341
Appendix C Implementations of UML Diagrams
Listing C.211 Java code implementing Client in figurs 9.21 and 9.22.
1 package se.leiflindback.oodbook.polyminherit.composite;
2
3 /**
4 * A definition of an algorithm.
5 */
6 public interface Task {
7 /**
8 * Performs the algorithm.
9 */
10 void performTask();
11 }
Listing C.212 Java code implementing Task in figurs 9.21 and 9.22.
1 package se.leiflindback.oodbook.polyminherit.composite;
2
3 /**
4 * An implementation of the algorithm.
5 */
6 public class ConcreteTaskA implements Task {
7 @Override
8 public void performTask() {
9 }
10 }
Listing C.213 Java code implementing ConcreteTaskA in figurs 9.21 and 9.22.
1 package se.leiflindback.oodbook.polyminherit.composite;
2
3 /**
4 * An implementation of the algorithm.
5 */
6 public class ConcreteTaskB implements Task {
342
Appendix C Implementations of UML Diagrams
7 @Override
8 public void performTask() {
9 }
10 }
Listing C.214 Java code implementing ConcreteTaskB in figurs 9.21 and 9.22.
1 package se.leiflindback.oodbook.polyminherit.composite;
2
3 import java.util.List;
4
5 /**
6 * A composite algorithm, containing concrete implementations
7 * of the algorithm.
8 */
9 public class Composite implements Task {
10 // The diagrams do not show how concrete tasks are added
11 // to the list.
12 private List<Task> tasks;
13
14 @Override
15 public void performTask() {
16 for (Task task : tasks) {
17 task.performTask();
18 }
19 }
20 }
Listing C.215 Java code implementing Composite in figurs 9.21 and 9.22.
1 package se.leiflindback.oodbook.polyminherit.tempmet;
2
3 /**
4 * A class that uses a concrete implementation of a template
5 * method.
6 */
7 public class Client {
8 private TaskTemplate task;
9 public void anyMethod() {
343
Appendix C Implementations of UML Diagrams
10 task.performTask();
11 }
12 }
1 package se.leiflindback.oodbook.polyminherit.tempmet;
2
3 /**
4 * The abstract superclass providing the template method.
5 */
6 public abstract class TaskTemplate {
7 public void performTask() {
8 // Some code, which is common for all concrete
9 // subclasses.
10 doPerformTask();
11 // Some code, which is common for all concrete
12 // subclasses.
13 }
14
15 /**
16 * Subclasses must provide an implementation of
17 * this method.
18 */
19 protected abstract void doPerformTask();
20 }
1 package se.leiflindback.oodbook.polyminherit.tempmet;
2
3 /**
4 * A concrete implementation of the template.
5 */
6 public class ConcreteTaskA extends TaskTemplate {
7 @Override
8 protected void doPerformTask() {
9 // Some code, which is specific for ConcreteTaskA.
10 }
11 }
1 package se.leiflindback.oodbook.polyminherit.tempmet;
344
Appendix C Implementations of UML Diagrams
2
3 /**
4 * A concrete implementation of the template.
5 */
6 public class ConcreteTaskB extends TaskTemplate {
7 @Override
8 protected void doPerformTask() {
9 // Some code, which is specific for ConcreteTaskB.
10 }
11 }
345
Bibliography
[LAR] C. Larman: Applying UML and Patterns, third edition, Prentice-Hall 2004,
ISBN:0131489062
[JU] Home page for the JUnit unit testing framework http://junit.org/
junit4/
[Code] A Git repository containing a NetBeans project with all the source code in
this text, https://github.com/oodbook/code The complete project
can also be downloaded as a zip file, https://github.com/oodbook/
code/archive/master.zip
346
Index
347
Index
guard, 37 package, 50
package diagram, 40
high cohesion, 46 package private, 50
immutable, 67, 163 pattern, 50, 51, 54, 55
implementation, 43 polymorphism, 175
inheritance, 10, 156, 182 primitive variables, excessive use, 96
integration, 15 programming, see coding
interaction diagram, 42, 58, 61 protected, 182
interaction operand, 23 public interface, 43
interaction use, 41 refactoring, 83
intercation operator, 23 reference, 4
interface, 9 requirements analyses, 14
iteration, 13
sequence diagram, 22, 40
javadoc, 8, 161 singleton, 211
@param, 8 spider-in-the-web, 33, 48, 69, 78
@return, 8 state, 51, 163
JUnit, 121, 123 static, 3, 40, 41
large class, 90 stereotype, 41, 150, 174
layer, 54 strategy, 199
lifeline, 22 subclass, 10
list, 5 super, 11
long method, 88 superclass, 10
long parameter list, 91 SUT, 121
low coupling, 48 system operation, 34, 52, 57, 61
system sequence diagram, 34
meaningless name, 101 system under test, 121
member, 40
message, 22 template method, 218
methodologies, 13 test, 14, 15, 120
model, 51 this, 3
MVC, 51 type, 12
348