Design-Patterns - Course-Notes PDF
Design-Patterns - Course-Notes PDF
Design-Patterns - Course-Notes PDF
All material in this course, unless otherwise noted, has been developed by
and is the property of the University of Alberta. The university has
attempted to ensure that all copyright has been obtained. If you
believe that something is in error or has been omitted, please contact us.
Design Patterns | 2
Table of Contents
Course Overview 5
Module 1: Introduction to Design Patterns: Creational &
Structural Patterns 6
Introduction to Design Patterns 6
Gang of Four’s Pattern Catalogue 8
Pattern Languages 8
Categories of Patterns 9
Singleton Pattern 10
Factory Method Pattern 12
Factory Objects 12
Benefits of Factory Objects 14
Factory Method Pattern 15
UML Diagrams 16
Façade Pattern 18
Step 1: Design the Interface 19
Step 2: Implement the Interface with one or more classes 20
Step 3: Create the façade class and wrap the classes that
implement the interface 20
Step 4: Use the façade class to access the subsystem 21
Adapter Pattern 22
Step 1: Design the target interface 23
Step 2: Implement the target interface with the adapter class 24
Step 3: Send the request from the client to the adapter using the
target interface 25
Composite Pattern 26
Step 1: Design the interface that defines the overall type 28
Step 2: Implement the composite class 29
Step 3: Implement the leaf class 30
Proxy Pattern 32
Step 1: Design the subject interface 34
Step 2: Implement the real subject class 34
Step 3: Implement the proxy class 35
Decorator Pattern 36
Step 1: Design the component interface 39
Step 2: Implement the interface with your base concrete
component class 40
Step 3: Implement the interface with your abstract decorator class
40
Step 4: Inherit from the abstract decorator and implement the
component interface with concrete decorator classes 41
Module 2: Behavioural Design Patterns 44
Template Method Pattern 44
Chain of Responsibility Pattern 47
State Pattern 50
Command Pattern 55
Purposes of the Command Pattern 55
Design Patterns | 3
Benefits of the Command Pattern 58
Observer Pattern 58
Module 3: Working with Design Patterns & Anti-patterns 62
MVC Pattern 62
Model 64
View 65
Controller 67
Design Principles Underlying Design Patterns 68
Open/Close Principle 68
Dependency Inversion Principle 71
Composing Object Principle 74
Interface Segregation Principle 76
Principle of Least Knowledge 78
Anti-Patterns & Code Smells 80
Comments 81
Duplicate Code 82
Long Method 82
Large Class 83
Data Class 83
Data Clumps 83
Long Parameter List 85
Divergent Class 85
Shotgun Surgery 86
Feature Envy 86
Inappropriate Intimacy 87
Message Chains 87
Primitive Obsession 87
Switch Statements 88
Speculative Generality 88
Refused Request 89
Course Resources 90
Course Readings 90
Glossary 90
Design Patterns | 4
Course Overview
Welcome to the second course in the Software Design and Architecture
specialization, brought to you in partnership by the University of Alberta
and Coursera. This course focuses on design patterns. Design issues in
applications can be resolved through design patterns commonly applied by
experts. This second course extends your knowledge of object-oriented
analysis and design by covering design patterns used in interactive
applications. Through a survey of established design patterns, you will gain
a foundation for more complex software applications. This course will also
explain several design principles that you can use to make your software
more reusable, flexible, and maintainable. Finally, you will learn how to
identify problematic software designs by referencing what is known as a
catalogue of code smells.
Design Patterns | 5
Module 1: Introduction to Design
Patterns: Creational &
Structural Patterns
Over the course of your career in software engineering, you will find that the
same design problems re-occur. There are many ways to deal with these
problems, but more flexible or reusable solutions are preferred in the
industry. One of these preferred methods is that of design patterns.
Design Patterns | 6
developers over many years. Design patterns outline solutions that often
create the best outcome.
It is important to understand that design patterns are not just a concrete set of
source code that you memorize and put into your software, like a Java library
or framework. Instead, design patterns are more conceptual. They are
knowledge that you can apply within your software design, to guide its
structure, and make it more flexible and reusable. In other words, design
patterns help software developers so that they have a guide to help them
solve design problems the way an expert might, so not everything needs to be
built from scratch. Design patterns serve almost like a coach to help
developers reach their full potential!
A strong advantage of design patterns is that they have already been proven
by experts. This means that you do not need to go through the trials they
have, and you go straight to creating better written software.
Design Patterns | 7
Gang of Four’s Pattern Catalogue
The four authors of the famous book Design Patterns: Elements of Reusable
Object-Oriented Software – Gamma, Helm, Johnson, and Vlissides – have
been collectively nicknamed the Gang of Four. These authors wrote their
book based on their own experiences as developers. When each had
developed programs and graphical applications, they had discovered
patterns emerging in their design solutions. They formalized those patterns
into a reference, which became their book.
The Gang of Four’s particular design pattern catalog is not actually like a
recipe book, as described in the last lesson, but is closer to a list of tropes.
You cannot pull universal patterns of the Gang of Four’s design pattern
catalog to use in your code, like a recipe. Instead, design patterns in
software the same way tropes re-occur in storytelling.
Learning to look for specific design patterns will help you better recognize
object-oriented design elsewhere.
Pattern Languages
The patterns and solutions of the Gang of Four’s catalog serve a variety of
different purposes. Depending on the context of the problem, you would
select a different pattern language to use. A pattern language is a
collection of patterns that are related to a certain problem space. For
example, the pattern language you select for designing accounting
software would be different than those you select for designing gaming
software. A pattern language for accounting software would include
double-entry bookkeeping, while a pattern language for gaming software
would include design words such as encounters, quests, and players.
Design Patterns | 8
Based on context, you must decide which ones will suit your problem or
design issue the best. However, sometimes you must consider trade-offs in
design – some patterns may be more resource intensive.
Categories of Patterns
Creational Patterns
Creational patterns deal with the creation or cloning new objects. Cloning
an object occurs when you are creating an object that is similar to an
existing one, and instead of instantiating a new object, you clone existing
objects instead of instantiating them.
The different ways of creating objects will greatly influence how a problem
is solved. Different languages therefore impact what patterns are possible
to use.
Structural Patterns
There are many different ways that you can structure objects depending on
the relationship you’d like between them. Not only do structural patterns
describe how different objects have relationships, but also how subclasses
and classes interact through inheritance. Structural patterns use these
relationships and describe how they should work to achieve a particular
design goal. Each structural pattern determines the various suitable
relationships among the objects.
Design Patterns | 9
different kinds of foods together: flavour determines what ingredients can
be mixed together to form a suitable relationship. Some relationships
combine ingredients together, such as chickpeas and garlic in hummus.
Some relationships still combine ingredients, but those ingredients may
maintain some independence, like a salad of mixed vegetables. Some
relationships allow a pairing of ingredients, without physical combination,
like wine and cheese.
Behavioural Patterns
Singleton Pattern
In a singleton design pattern only has one object of a class. This might be
desirable in order to circumvent conflicts or inconsistencies, by keeping
clear which object the program should draw from. For example, the
Preferences of an app, the print queue of your printer, the software driver
for a device are objects that only having one of is preferable. If there are
multiple instances, it can be confusing for the program output.
Another goal of singleton design pattern is that the single object is globally
accessible within the program.
Design Patterns | 10
Public class notSingleton {
//Public Constructor
Public notSingleton() {
..
}
}
Instead, if the class has a private constructor, then the constructor cannot
be called from outside the class. Although this seems to prevent creating an
object of the class, there are two key components for getting around this.
Second, create a public method in the class that will create an instance of
this class, but only if an instance does not exist already. In this case, the
method is called “getInstance”. It will check if the “uniqueInstance” variable
is null. If it is null, then it will instantiate the class and set this variable to
reference the object. On the other hand, if the “uniqueInstance” class
variable currently references an object, meaning that there is already an
object of this class, then the method will simply return that object. As the
“getInstance” method is public, it can be called globally and used to create
one instance of the class. In a sense, it replaces the normal constructor.
Private ExampleSingleton() {
…
}
Return uniqueInstance;
…
}
In the example above, the regular constructor is hidden. Other classes are
Design Patterns | 11
forced to call the public “getInstance” method. This puts in place basic
gatekeeping, and ensures that only one object of this class is created. The
same method can be used to globally reference the single object if it has
already been created.
There are trade-offs to the Singleton design principle. If there are multiple
computing threads running, there could be issues caused by the threads
trying to access the shared single object.
Factory Objects
A factory object operates like a factory in the real world, and creates
objects. Factory objects makes software easier to maintain and change,
because object creation happens in the factories. The methods that use
these Factories can then focus on other behaviour.
However, imagine that the store is successful and adds more knife types to
sell. New subclasses will need to be added, such as BreadKnife,
ParingKnife, or FilletKnife. The list of conditionals would need to grow and
grow as new knife types are added. However, the methods of the knife
Design Patterns | 12
after it is created, such as sharpening, polishing, and packaging, would
likely stay the same, no matter the type of knife. This creates a complicated
situation. Instead of making Knives in the store, it may be better to create
them in a Factory object.
Below is an example of the code that might be used for this example:
return knife;
}
}
The code to create a Knife object has been moved into a method of a new
class called Knife Factory. This allows the KnifeStore class to be a client for
the Knife Factory. The KnifeFactory object to use is passed into the
constructor for the KnifeStore class. Instead of performing concrete
instantiation itself, the orderKnife method delegates the task to the factory
object.
Knife knife;
Design Patterns | 13
//use the create method in the factory
knife = factory.createKnife(knifeType);
return knife;
}
}
There are numerous benefits to using factory objects. One of these benefits
is that it is much simpler to add new types of an object to the object factory
without modifying the client code. Instead of hunting down multiple
snippets of similar instantiation code, subclasses can simply be added or
removed in the Factory. This only requires changing code in the Factory, or
to the concrete instantiation, and not the client method.
The use of the word “factory” serves a good metaphor here. Using Factory
objects is similar to using a factory in real-life to create knives – the stores
do not usually make the names themselves, but get them from a factory. In
fact, a factory may have multiple clients that they make knives for! If the
factory changes how knives are made, the stores themselves will not care
as long as they still receive knives of the right types.
Essentially, using factory objects means that you have cut out redundant
code and made the software easier to modify, particularly if there are
multiple clients that want to instantiate the same set of classes. In other
words, if many parts of the software want to create the same objects,
factory objects are useful tools. Subclasses of the factory class can even
become their own specialized factories!
Design Patterns | 14
Factory Method Pattern
The factory object is not actually a design pattern onto itself. The Factory
Method pattern does not use a Factory object to create the objects,
instead, the Factory Method uses a separate “method” in the same class to
create objects. The power of Factory Methods come in particular from how
they create specialized product objects.
knife.sharpen();
knife.polish();
knife.package();
return knife;
}
Design Patterns | 15
method.
UML Diagrams
A class’ subclasses will all inherit from the same method. However, each
subclass must define their own methods as well. This can be represented
using UML diagrams. Let us examine a UML diagram based on the Knife
Store example used throughout this lesson.
Design Patterns | 16
In this diagram, the “Knife” and “KnifeStore” classes are italicized to
indicate that they are abstract classes that cannot be instantiated. Instead,
their subclasses must be defined. In this case, there is only one subclass for
Knife, but you could several more. Similarly, there is only one KnifeStore in
this example, but theoretically, several more could exist, that would make
other types of knives. Note that the createKnife() method is also abstract
under the KnifeStore class. This indicates that any subclass of Knife Store
must define this method.
This structure is the core of the Factory Method design pattern. We can
abstract out this example to demonstrate a more general structure.
There should be an abstract Creator class, that contains methods that only
operate on generalizations. The Factory Method is declared by the Creator
abstractly, so that each Concrete Creator class is obliged to provide a
Factory Method.
Design Patterns | 17
The methods of the Concrete Creator class only operate on the general
Product, never the Concrete Products. The Concrete Products are made by
the Concrete Creator. The type of product made is decided by which
Concrete Creator is made.
Façade Pattern
Design Patterns | 18
simplify the interaction with a subsystem for client classes, and if there is a
need for a class to instantiate other classes within your system and to
provide these instances to another class. Often façade design patterns
combine interface implementation by one or more classes, which then gets
wrapped by the façade class. This can be explained through a number of
steps.
Let us examine each of these steps with an example for a bank system.
In the UML diagram below, we can see that a BankService class acts as a
façade for Chequing, Saving, and Investment classes. As all three accounts
implement the iAccount interface, the BankService class wraps the account
interface and classes, presenting a simpler “front” for the Customer client
class.
Let us look at how to do this with code for each step outlined above.
Design Patterns | 19
public interface IAccount {
public void deposit(BigDecimal amount);
public void withdraw(BigDecimal amount);
public void transfer(BigDecimal amount);
public int getAccountNumber();
}
Implement the interface with classes that will be wrapped with the façade
class. Note that in this simple example, only one interface is being
implemented and hidden, but in practice, a façade class can be used to
wrap all the interfaces and classes for a subsystem.
Step 3: Create the façade class and wrap the classes that
implement the interface
public BankService() {
this.bankAccounts = new Hashtable<int,
IAccount>
}
Design Patterns | 20
IAccount newAccount = null;
switch (type) {
case "chequing":
newAccount = new
Chequing(initAmount);
break;
case "saving":
newAccount = new
Saving(initAmount);
break;
case "investment":
newAccount = new
Investment(initAmount);
break;
default:
System.out.println("Invalid
account type");
break;
}
if (newAccount != null) {
this.bankAccounts.put(newAccount.getAc
countNumber(), newAccount);
return newAccount.getAccountNumber();
}
return -1;
}
With the façade class in place, the client class can access accounts through
the methods of the BankService class. The BankService class will tell the
client what type of actions it will allow the client to call upon, and then will
delegate that action to the appropriate Account object.
Design Patterns | 21
BigDecimal(1000.00));
myBankService.transferMoney(mySaving,
myInvestment, new BigDecimal(300.00));
}
}
Adapter Pattern
A client class. This class is the part of your system that wants to use
a third-party library or external systems.
An adaptee class. This class is the third-party library or external
system that is to be used.
An adapter class. This class sits between the client and the adaptee.
The adapter conforms to what the client is expecting to see, by
implementing a target interface. The adapter also translates the
client request into a message that the adaptee will understand, and
Design Patterns | 22
returns the translated request to the adaptee. The adapter is a kind
of wrapper class.
A target interface. This is used by the client to send a request to the
adapter.
Design Patterns | 23
First, create the target interface that your adapter class will be
implementing for your client class to use:
The adapter class provides the methods that will take the client class’s
object and convert it into a JSON object. The adapter should convert any
instance of a class that the client can create and send that in a request. The
adapter class also transfers the translated request to the adaptee. The
client class therefore only needs to know about the target interface of the
adapter.
Design Patterns | 24
Step 3: Send the request from the client to the adapter
using the target interface
The web client normally returns an object back to the client. In light of this,
the doWork method should not be modified as it may disrupt other parts of
the system. Instead, the Web Client should perform this behaviour as
normal, and add in a send message method, where you can pass in the
adapter, the web service, and any message you want to send.
if (status == 200) {
System.out.println("OK");
} else {
System.out.println("Not OK");
}
return;
}
}
Design Patterns | 25
Although it might be tempting to think that a solution is to simply change
an interface so that it is compatible with another one, this is not always
feasible, especially if the other interface is from a third-party library or
external system. Changing your system to match the other system is not
always a solution either, because an update by the vendors to the outside
systems may break part of our system.
Composite Pattern
Design Patterns | 26
Instructor’s Note: An abstract superclass can also be used in
place of an interface, as both allow for
polymorphism. However, this lesson will focus on
interfaces.
The Leaf class and the Composite class implement the Component
interface, unifying them with a single type. This allows us to deal with non-
composite and composite objects uniformly – the Leaf class and the
Composite class are now considered subtypes of Component.
You may have other composite or leaf classes in practice, but there will only
be one overall component interface or abstract superclass.
This design pattern has a composite class with a cyclical nature, which may
make it difficult to visualize. Instead, it is easier to think of composite
design patterns as trees:
Design Patterns | 27
have the ability to “grow” a tree, while a leaf ends the three where it is.
Begin by defining the interface that the composite and leaf classes will
implement. This supports polymorphism for your component and leaf
classes.
Design Patterns | 28
public String getName();
}
Implement the interface in the composite class. This will give the Housing
class its own behaviour when client code uses it.
Design Patterns | 29
public void enter() { … }
public void exit() { … }
}
The final step is to implement the leaf class. The leaf class does not contain
any components, and does not need to have a collection of components
added to it, or any methods to manage such a collection. Instead, the
methods of iStructure interface simply need to be implemented.
In this example, the building has one floor with a common room and two
washrooms. See the code below.
Design Patterns | 30
public class Program {
int firstMens =
floor1.addStructure(washroom1m);
int firstWomans =
floor1.addStructure(washroom1w);
int firstCommon =
floor1.addStructure(common1);
Composite objects can be built quickly and easily. Each component can be
expected to have the same set of behaviours without needing to do any
type checking.
Design Patterns | 31
objects to be composed of other objects that are of a common
type.
Proxy Pattern
In this design pattern, the proxy class wraps the real subject class. This
means that a reference to an instance of the real subject class is hidden in
the proxy class. The real object is usually a part of the software system that
contains sensitive information, or that would be resource intensive to
instantiate. As the proxy class is a wrapper, client classes interact with it
instead of the real subject class.
The three most common scenarios where proxy classes are used are:
Design Patterns | 32
The proxy class wraps and may delegate, or redirect, calls upon it to the real
subject class. However, not all class are delegated, as the proxy class can
handle some its lighter responsibilities. Only substantive requests are sent
to the real subject class. Because of this, the proxy class must offer the
same methods. Having both these classes implement a common subject
interface allows for polymorphism.
Note that the proxy and real subject classes are subtypes of the Subject. A
client class can therefore interact with the proxy, which will have the same
expected interface as the real subject.
Below is an example of a UML diagram for an online retail store with global
distribution and warehousing. In this scenario, you need to determine
which warehouse to send orders to. A system that routes orders for
fulfillment to an appropriate warehouse will prevent your warehouses from
receiving orders that they cannot fulfill. A proxy may protect your real
subject, the warehouses, from receiving orders if the warehouses do not
have enough stock to fulfill an order.
Design Patterns | 33
The Order Fulfillment class is the proxy in this example. Clients interact
with the system by using the interface.
First, create the interface that client software will use to interact with your
system.
Design Patterns | 34
}
The final step requires implementing the proxy class. In this case, the Order
Fulfillment class checks warehouse inventory and ensures that an order can
be completed before sending requests to the warehouse. To do this, it asks
each warehouse if it has enough stock of a particular item. If a warehouse
does, then the item gets added to a new Order object that will be sent to
the Warehouse. The Order Fulfillment class also lets you separate order
validation from the order fulfillment by separate them into two pieces. This
improves the overall rate of processing an order, as the warehouse does not
have to worry about the validation process or about re-routing an order if it
cannot be fulfilled.
The Order Fulfillment class can be improved with other functionalities, such
as prioritizing sending orders to warehouses based on proximity to the
customer.
}
}
Key responsibilities of the proxy class there include protecting the real
subject class by checking the client’s request and controlling access to the
real subject class, and acting as a wrapper class for the real subject class.
Design Patterns | 35
In summary, a proxy design pattern allows to defer creating resource
intensive objects until needed, control access to specific objects, or when
you need something to act as a local representation of a remote system.
They made systems more secure, and less resource intensive. The main
features of a proxy design pattern are:
Decorator Pattern
Design Patterns | 36
In this visual representation, object A is a base object, as it does not contain
another object, and has its own set of behaviours. Object B aggregates
object A, allowing object B to “augment” the behaviours of object A.
Objects can continue to be added to the stack. So, in the above diagram,
object C aggregates to object B and augments the behaviours of object B.
This design pattern makes use of interfaces and inheritance, so that the
classes conform to a common type whose instances can be stacked in a
compatible way. This builds a coherent combination of behaviour overall.
Design Patterns | 37
It consists of a Component interface that defines the common type for all
the classes. A client class expects the same interface across all the
component classes. A concrete component class implements the
component interface and can be instantiated. An instance of this class can
be used as a base object in the stack.
Design Patterns | 38
When expressed as a UML diagram, the example outlined above looks as
below:
Note that for simplicity, the BasicWebPage’s HTML, stylesheet, and scripts
are represented as Strings. As well, any number of additional behaviours
can be used to augment the basic web page, but this example is limited to
the behaviours of adding user authorization, and authentication to make
sure the user is who they claim to be.
Implementation of this design pattern with Java can be broken down into
steps.
First, define the interface that the rest of the classes in the design pattern
will be subtypes of. This interface will define the common behaviours that
your basic web page and decorators will have.
Design Patterns | 39
Step 2: Implement the interface with your base concrete
component class
Next, implement the base concrete component class with the interface.
The concrete component class will be the base building block for all web
pages objects during run time.
The concrete component class will implement how it displays itself by using
standard HTML markup and page element styling defined in the cascading
style sheet. This example web page will also run some basic JavaScript.
The web page decorator only has one instance of web page. This allows
decorators to be stacked on top of the basic web page, and on top of each
other. Each type of web page is responsible for its own behaviour and will
recursively invoke the next web page on the stack to execute its behaviour.
In stacking, the order in which you build the stack matters. However, the
Design Patterns | 40
basic web page must be the first one in the stack. The rest of the ordering
will depend on the design of your system and which augmenting
behaviours you want executed first.
The abstract decorator simply delegates the display behaviour to the web
page object that it aggregates. This allows you to combine the display
behaviour down the stack of web pages.
The final step is to inherit from the abstract decorator and implement the
component interface with the concrete decorator classes.
public display() {
super.display();
this.authorizedUser();
}
}
public display() {
super.display();
this.authenticateUser();
}
}
Design Patterns | 41
Notice that to recursively call the display behaviour, the concrete
decorators invoke the superclass’ display method. Since the abstract
decorator superclass facilitates the aggregation of various types of web
design, the call to super.display will cause the next web page in the stack to
execute its version of display until you get to the basic web page. The
recursive call will end here because the basic web page is the concrete
component, which does not aggregate any other types of web pages. This
links the calls of display all the way to the bottom, and bubbles the
execution back up. The basic web page must be built before behaviours can
be added to it.
As explained above, the basic web page is built first. The authorization
behaviour is added next. The web page is completed by adding the
authentication behaviour lsat.
When the display method is called, it will link the method calls down to the
basic web page. The basic web page will display itself and then the display
call will move back up the links to the authorized behaviour first and the
authentication behaviour last. As an aggregation stack diagram, this would
look as follows:
Design Patterns | 42
Any decorator could be added to the basic web page to create a different
combined behaviour. The basic web page’s behaviour can be dynamically
built up in this way.
Use of design patterns like the decorator pattern help you to create
complex software, without the complex overhead.
Design Patterns | 43
Module 2: Behavioural Design Patterns
This module will describe certain behavioural design patterns. These are
patterns that focus on ways that individual objects collaborate to achieve a
common goal. This is an important aspect to consider in software design –
objects are only pieces of a larger solution. For each to work effectively, it
must have a set purpose. Let us examine some behavioural design patterns.
The template method is best used when you can generalize between two
classes into a new superclass. Think of it like another technique to use when
you notice you have two separate classes with very similar functionality and
order of operations. You can choose to use a template method, so that
changes to these algorithms only need to be applied in one place instead of
two. The template method would be within the superclass, and would
therefore be inherited by the subclasses. Differences in algorithms would
be done through calls to abstract methods whose implementations are
provided by the subclass. After using generalization, objects can be more
effectively reused. Inheritance allows functionality to be shared between
classes, and enables clearer and more self-explanatory code.
Design Patterns | 44
Both dishes require that you boil water, cook the pasta, add the sauce, add
the protein, and garnish the plate. Some of these steps have different
implementation depending on which dish is being prepared – each dish has
a different sauce, protein, and garnish. Other steps, however, would be the
same, such as boiling the water.
This situation could be modeled with a PastaDish class that has a method
that makes the recipe for each subclass, spaghetti with meatballs or penne
alfredo. The method knows the general set of steps to make either dish,
from boiling water to adding the garnish. Steps that are special to a dish,
like the sauce to add, are implemented in the subclass. The UML diagram
below illustrates this example.
The UML for the template method pattern is centred upon the relationship
between the superclass and its subclasses. The superclass must contain
abstract methods that are implemented by the subclasses.
First, let us examine a look at the code for the Pasta-Dish superclass.
Design Patterns | 45
final void makeRecipe() {
boilWater();
addPasta();
cookPasta();
drainAndPlate();
addSauce();
addProtein();
addGarnish();
}
abstract void addPasta();
abstract void addSauce();
abstract void addProtein();
abstract void addGarnish();
From within the make-recipe, the other methods are called. Methods that
are the same for any subclass are consolidated in the superclass.
Specialized methods, however, are left up to subclasses to provide them.
Design Patterns | 46
}
}
When a client object sends a request, the first handler in the chain will try to
process it. If the handler can process the request, then the request ends
with this handler. However, if the hander cannot handle the request, then
the request is sent to the next handler in the chain. This process will
continue until a handler can process the request.
If the entire chain is unable to handle the request, then the request is not
satisfied.
Design Patterns | 47
each tool and try it one at a time on the screw, until you could determine
which one of them works.
The chain of responsibility can be used for many different purposes. For
example, it is commonly used for filtering objects, such as putting certain
emails into a spam folder.
The UML diagram for the chain of responsibility typically looks as below.
Objects on the chain are handlers that implement a common method
handleRequest(), declared in the abstract superclass Handler, as indicated
by the italics. Handle objects are connected from one to the next in the
chain. Subclasses of Handler handle requests in their own way.
Design Patterns | 48
The chain of responsibility pattern does not come without potential issues.
For example, what if there’s a mistake in the second filter, and its rule
doesn’t match but forgets to pass the request on to the next filter. In this
case, the handling ends prematurely. To circumvent this problem, there
needs to be an algorithm that ensures each filter class handles requests in a
similar fashion.
Design patterns are not without problems! In fact, design patterns often have
their own internal problems. It is very common in the industry to combine
design patterns to fix those problems.
Design Patterns | 49
State Pattern
Objects in your code are aware of their current state. They can choose an
appropriate behaviour based on their current state. When their current
state changes, this behaviour can be altered. This is the state design
pattern.
This pattern should be primarily used when you need to change the
behaviour of an object based upon changes to its internal state or the state
it is in at run-time. This pattern can also be used to simplify methods with
long conditionals that depend on the object’s state.
If you insert the dollar into the machine, several things might happen. You
could make your selection, the machine would dispense a chocolate bar,
and you would collect the bar and leave. You could decide you no longer
want the chocolate bar, press the eject money button, and the machine
would return the dollar. You could make your selection, the machine could
be out of chocolate bars, and could notify you of this.
There are three different states that the vending machine can be in within
this scenario. Before approached, the machine is in an idle state. When the
dollar is inserted, the state of the machine changes, and could either
dispense a product or return the money upon receiving an eject money
Design Patterns | 50
request. The third state the machine could be in would be if the machine is
out of stock.
But, applying the state design pattern as a UML diagram presented at the
beginning of this lesson, the following diagram can be generated:
Design Patterns | 51
Let us examine the state interface as code. State classes must implement
the methods in this interface to respond to each trigger.
vendingMachine.setState(
vendingMachine.getHasOneDollarState()
);
}
Design Patterns | 52
System.out.println( "payment required" );
}
}
The Idle-State class now implements the State interface. When a dollar is
inserted, the insertDollar method is called, which then calls a setState
method upon the vendingMachine object. This changes the current state of
the machine to the HasOneDollar state.
vendingMachine.doReturnMoney();
vendingMachine.setState(
vendingMachine.getIdleState()
);
}
if (vendingMachine.getCount() > 1) {
vendingMachine.doReleaseProduct();
vendingMachine.setState(
vendingMachine.getIdleState()
);
} else {
vendingMachine.doReleaseProduct();
vendingMachine.setState(
vendingMachine.getOutOfStockStat
e()
);
}
}
}
Design Patterns | 53
If the dispense method is called in the HasOneDollarState class, then the
product is released. If stock remains after this, the vending machine’s state
sets to either the Idle state or the Out-Of-Stock state.
if (count > 0) {
currentState = idleState;
this.count = count;
} else {
currentState = outOfStockState;
this.count = 0;
}
}
The Vending-Machine class would have methods to handle the triggers, but
delegates the handling to whatever is the current state object.
Using the State design pattern, long conditionals are not necessary in the
methods, and creates much cleaner code.
Design Patterns | 54
Command Pattern
There are many different purposes for using the command pattern.
One is to store and schedule different requests. If requests are turned into
command objects in your software, then they can be stored into lists and
manipulated before they are completed. They can also be placed onto a
queue so that different commands can be scheduled to be completed at
different times. For example, the command pattern can be used to have an
alarm ring in calendar software. A command object could be created to ring
the alarm, and this command could be placed into a queue, so that it is
completed when the event is scheduled to occur.
Design Patterns | 55
be used to put commands that have been undone. Each time a command is
requested, a command object is created and executed. When the command
is completed, it goes to the history list. If you undo a command, then the
software would go to the history list and ask the most recent command
executed to undo itself, and put it on the redo list. Alternately, if a user
needs to redo, the software would take the most recent command undone
in the redo list, and move it onto the history list again. The redo list will be
emptied every time a command is executed, because usually you can’t redo
a previous edit after a new edit has been made.
The command pattern lets you do things to requests that you wouldn’t be
able to do if they were simple method calls from one object to the other.
Commands can also be stored in a log list, so that if the software crashes
unexpectedly, users can redo all the recent commands.
Design Patterns | 56
The concrete command classes call on specific receiver classes to deal with
the work of complete the command.
Let us examine how a command object should be written in Java code, with
the example of “pasting text”.
The paste command extends the Command superclass, and keeps track of
where the text will be inserted, and what will be inserted. Command
objects must be able to keep track of a lot of details on the current state of
the document in order for commands to be reversible.
When the execute() and unexecute() methods are called, this is where the
command object actually calls on the receiver to complete the work.
CommandManager commandManager =
CommandManager.getInstance();
commandManager.invokeCommand (
command );
Design Patterns | 57
The invoker references the command manager, which is the object that
manages the history and redo lists. Then, the invoker creates the command
object with the information needed to complete the command. Finally, it
calls the command manager to execute the command.
The command pattern also allows logic to be pulled from user interfaces.
User interface classes should only be dealing with issues like getting
information to and from the user, and application logic should not be in
user interface classes. The command pattern creates a layer where
command objects go, so that every time a button is clicked on the
interface, a command object is created. This is where application logic will
sit instead. The command objects are independent of the user interface, so
that adding changes like new buttons to the interface is easier and faster.
Each and every service in a system can be an object of its own, allowing for
more flexible functionality. This pattern can be a great asset to making
versatile and easy-to-maintain software programs.
Observer Pattern
Design Patterns | 58
The sequence diagram for this example might look as below:
A sequence diagram for observe patterns will have two major roles: the
subject (the blog) and the observer (a subscriber). In order to form the
subject and observer relationship, a subscriber must subscribe to the blog.
The blog then needs to be able to notify subscribers of a change. The notify
function keeps subscribers consistent, and is only called when a change has
been made to the blog. If a change is made, the blog will make an update
call to update subscribers. Subscribers can get the state of the blog through
a getState call. It is up to the blog to ensure its subscribers get the latest
information.
To unsubscribe from the blog, subscribers could use the last call in the
sequence diagram. unsubscribe() originates from the subscriber and lets
the blog know the subscriber would like to be removed from the list of
observers.
Design Patterns | 59
The Subject superclass has three methods: register observer, unregister
observer, and notify. These are essential for a subject to relate to its
observers. A subject may have zero or more observers registered at any
given time. The Blog subclass would inherit these methods.
The Observer interface only has the update method. An observer must
have some way to update itself. The Subscriber class implements the
Observer interface, providing the body of an update method so a subscriber
can get what changed in the blog.
Design Patterns | 60
In the Subject superclass, the registerOberserver method adds an observer
to the list of observers. The unregisterObserver method removes an
observer from the list. The notify method calls update upon each observer
on the list.
The Observer interface makes sure all observer objects behave the same
way. There is only a single method to implement, update(), which is called
by the subject. The subject makes sure when a change happens, all its
observers are notified to update themselves. In this example, there is a
class Subscriber the implements the Observer interface.
This update method is called when the blog notifies the subscriber of a
change.
Design Patterns | 61
Module 3: Working with Design Patterns
& Anti-patterns
MVC Pattern
Design Patterns | 62
responsibilities of a system that offers a user interface into three parts:
model, view, and controller.
The Model is going to contain the underlying data, state, and logic that
users want to see and manipulate through an interface. It is the “back end”,
or the underlying software. A key aspect of the MVC pattern is that the
model is self-contained. It has all of the state, methods, and other data
needed to exist on its own. The View gives a user the way to see the model
in the way they expect, and allows them to interact with it, or at least parts
of it. It is the “front end” or the presentation layer of the software. A model
could have several views that present different parts of the model, or
present the model in different ways.
When a value changes in the model, the view needs to be notified so it can
update itself accordingly. The observer design pattern allows this to
happen. In an observer pattern, observers are notified when the state of the
subject changes. In this case, the view is an observer. When the model
changes, it notifies all of the views that are subscribed to it.
The view may also present users with ways to make changes to the data in
the underlying model. It does not directly send requests to the model,
however. Instead, information about the user interaction is passed to a
Controller. The controller is responsible for interpreting requests and
interaction with elements in the view, and changing the model. The view is
therefore only responsible for the visual appearance of the system. The
model focuses only on managing the information for the system.
Design Patterns | 63
The MVC pattern uses the separation of concerns design principle to
divide up the main responsibilities in an interactive system. The controller
ensures that the views and the model are loosely coupled. The model
corresponds to entity objects, which are derived from analyzing the
problem space for the system. The view corresponds to a boundary object,
which is at the edge of your system that deals with users. The controller
corresponds to a control object, which receives events and coordinates
actions.
Model
Let us begin with the model, which is the most essential part of the pattern.
The model should be able to exist independently, without views or
controllers.
In this example, since the view is going to be an observer, the model needs
to be observable. The java.util package can be used to implement this
behaviour. This package contains an observable class that can be extended.
In this example, Observable can be extended, and the StoreOrder class will
allow you to add your views as observers. This will allow them to update
whenever the order is updated.
import java.util.*;
public StoreOrder() {
itemList = new ArrayList<String>();
priceList = new ArrayList<BigDecimal>();
}
Design Patterns | 64
ListIterator<String> itemItr =
itemList.listIterator();
return itemItr;
}
...
setChanged();
notifyObservers();
}
This simple example is a class that has several methods that modify itself.
Items can be added, deleted, or have their prices change. When changes
are made, the set Change method flags the change, the notify Observers
method notifies the views so that they can update themselves.
Note that the model does not contain any user interface elements, and is
not aware of any views.
View
Design Patterns | 65
more type-safe, you could alternately develop your own Observer pattern
with Generics.
The view cannot call the method of the Model, but instead calls the
methods of the Controller. This is what happens when the view tries to
modify the model.
import java.util.*;
import javax.swing.JFrame;
// ..etc.
// Controller
private OrderController controller;
// User-Interface Elements
private JFrame frame;
private JButton changePriceButton;
private JButton deleteItemButton;
private JTextField newPriceField;
private JLabel totalLabel;
private JTable groceryList;
...
...
}
display(((StoreOrder) s).getItemList(),
((StoreOrder) s).getPriceList());
Design Patterns | 66
ArrayList priceList ) {
// code to display order
...
}
controller.deleteItem(groceryList.getSelectedRow()
);
}
else if (event.getSource() ==
changePriceButton) {
BigDecimal newPrice = new
BigDecimal(newPriceField.getText());
controller.changePrice(groceryList.get
SelectedRow(),newPrice);
}
}
Controller
This example is very simple, but still demonstrates that the controller must
have references to both the view and model that it connects. Notice also
that the controller does not make changes to the state of the model
directly. Instead, it calls methods of the model to make changes.
Design Patterns | 67
Controllers make the code better in the following ways:
The view can focus on its main purpose: presenting the user
interface, as the controller takes the responsibility of interpreting
the input from the user and working with the model based on that
input.
The view and the model are not “tightly coupled” when a controller
is between them. Features can be added to the model and tested
long before they are added to the view.
Controllers make the code cleaner and easier to modify. Not that in most
software systems, there are generally multiple model, view, and controller
classes.
MVC patterns may be used in many ways. The defining feature of the MVC
pattern is the separation of concerns between the back-end, the front-end,
and the coordination between the two.
The MVC pattern is particularly important because of its use of the separation
of concerns. It allows the program to be modular and loosely coupled. Loose
coupling is key for large development teams – it allows different teams to work
on different parts, so one team can work on the only the view, and another
team can work on only the model. The view and the model can change,
without needing knowledge of the other.
All design patterns follow a basic set of design principles. This next lesson
will examine some of these underlying design principles. These principles
address issues such as flexibility and reusability.
Open/Close Principle
The open/closed principle states that classes should be open for extension
but closed to change.
Design Patterns | 68
expected.
All the attributes and behaviours are encapsulated,
Been proven to be stable within your system. The class or any
instance of the class should not stop your system from running or
do it harm.
Although the principle is called “closed”, it does not mean that changes
cannot be made to a class during development. Things should change
during the design and analysis phase of your development cycle. A “closed”
class occurs when a point has been reached in development when most of
the design decisions have been finalized and once you have implemented
most of your system.
Design Patterns | 69
If you want to limit a class so that it is no longer extendable, it can be
declared as “final” to prevent further inheritance. This keyword can also be
used for methods.
The second way a class can be open, is if the class is abstract and enforces
the open/close principle through polymorphism. An abstract class can
declare abstract methods with just the method signatures. Each concrete
subclass must provide their own implementation of these methods. The
methods in the abstract superclass are preserved, and the system can be
extended by providing different implementations for each method. This is
useful for behaviours like sorting or searching.
All design patterns use the open/close principle in some way. They all follow
the idea that some part of your system should be able to extend and to be
built upon through some means like inheritance or implementing an
interface. It may not always be possible to practice this principle, but it is
something to strive towards.
Design Patterns | 70
interfaces.
All the design patterns covered in this course are built on this principle.
Design Patterns | 71
public class ClientSubsystem {
public QuickSorting enterpriseSorting;
/* Constructors and other attributes go here */
this.enterpriseSorting.quickSort(customerList);
}
}
This has low level dependency. There is direct naming and referencing to
the concrete class, QuickSorting, which is used in this example to sort a list
sent to the system by a user. However, if a different sorting class called
MergeSort were to be implement, significant changes would need to be
made to the ClientSubsystem class. The type of sorting class would have to
change, as well as the sorting method call. This would require a great deal
of work every time a change needed to be made to the sorting algorithm.
this.enterpriseSorting.mergeSort(customerList);
}
}
Such large changes are impractical, and could entail unexpected side
effects if old references are missed. The dependency inversion principle
addresses this issue by generalizing low level functionality into interfaces or
abstract classes. This way, when alternative implementations are offered,
they can be used with ease.
High level dependency prevents the client class from being directly coupled
with a specific concrete class. A client class is dependant on expected
behaviours, not on a specific implementation of behaviours.
When the dependency inversion principle and high level dependency are
used, the overall architecture of a system will look similar to design
patterns explored earlier in this course. The diagram below illustrates this.
Design Patterns | 72
Behaviours are generalized across each subsystem into an interface. The
concrete classes of each subsystem will then implement the interface.
Client classes will make references to the interface instead of directly to the
concrete classes.
public ClientSubsystem(Sorting
concreteSortingClass) {
this.enterpriseSorting =
concreteSortingClass;
}
Design Patterns | 73
implementations of functionality. It is best practice to program to
generalizations, like interfaces and abstract classes, rather than directly to
a concrete class.
Using the dependency inversion principle may seem like extra work, but the
effort is worthwhile, particularly if you are working on a large system. If a
system is too heavily coupled, changes will be difficult to make. This
principle helps ensure a robust software solution.
Design patterns like the composite design pattern and decorator design
pattern use this design principle. These two patterns compose concrete
Design Patterns | 74
classes to build more complex objects at run time. The overall behaviour
comes from the composed sum of the individual objects. An object can
reuse and aggregate another object, to delegate certain requests to it. The
system should be designed so that concrete classes can delegate tasks to
other concrete classes.
Design Patterns | 75
In summary, the composing objects principle will:
Some good questions to help you decide whether the best solution for your
system is composition or inheritance include:
Many design patterns that this course has explored use generalizations of
concrete classes, presented as interfaces for client classes to use indirectly
invoke the behaviours of the concrete classes. This helps client classes be
less dependent on concrete classes, and allows for changes to be made
more easily. In general, your design should strive to program to interfaces
instead of concrete classes.
However, if there is only a single interface that holds all the methods for all
the clients, then all clients will have to unnecessarily implement all other
clients’ methods just to make their interface compile. Not all behaviours are
shared by clients, so why should they be stored in the same interface? This
forces dependency.
The interface segregation principle helps tackle this issue so that you
won’t run into dependency issues or be forced to generalize everything into
one interface. The principle states that a class should be forced to depend
on methods it does not use. This means that any classes that implement an
interface should not have “dummy” implementations of any methods
defined in the interfaces. Instead, large interfaces should be split into
Design Patterns | 76
smaller generalizations.
A poor interface design would entail a single interface for both the Human
Cashier class and the Self Serve Machine class. This interface would include
all the methods for both classes, whether or not they fit both classes. These
would be “dumb” implementations that do not do anything depending on
which class called upon the interface.
A good interface design would apply the interface segregation principle and
split the interface into two smaller ones. This allows each interface to be
more accurate with its description of expected behaviours, and select the
correct combinations of interfaces a concrete class should implement.
Represented as a UML diagram, this would look as below.
The Human-Cashier class can implement both interfaces, and the Self-
Serve-Machine class would only need to implement the I-Cashier interface.
This way, both concrete classes only need to provide implementations for
Design Patterns | 77
the interfaces that generalize their specific functionality. The interfaces
should be kept separate, and the Human Cashier class can implement two
interfaces. This is preferable to have an I-Human-Worker interface that
inherits the I-Cashier interface, as the I-Human-Worker interface would
inherit behavioural descriptions of a cashier, which may not always apply to
an employee. Remember, the single inheritance rule in Java applies to
classes, but not to interfaces. An interface can inherit from as many
interfaces as it wants to.
Note that it will not always be clear how to properly segregate your
interfaces, or to predict future changes in requirements that will need
interfaces to be split up. Well-defined interfaces are important – interfaces
are descriptions of what parts of your system can do. The better the
description the easier it will be to create, update, and maintain software.
The principle of least knowledge states that classes should know about
and interact with as few other classes as possible. This principle arises from
one means of managing complexity, which suggests that a class should be
designed so that it does not need to know about and depend upon almost
every other class in the system. If classes have a narrow view of what other
classes it knows, then coupling is decreased and a system is easier to
maintain.
This principle is also realized in a rule known as the Law of Demeter. The
Law of Demeter is composed of several different rules. These rules provide
a guideline as to what kind of method calls a particular method can make –
in other words, they help determine how classes should interact. This
design principle focuses on how method calls should be made, rather than
on specific ways of structuring the design of a system.
Design Patterns | 78
M, of an object should only call other methods if they are:
The first rule states that a method, M, in an object, O, can call on any other
method within O itself. In other words, a method encapsulated within a
class is allowed to call any other method also encapsulated within the same
class.
The second rule states that a method, M, can call the methods of any
parameter P. As a method parameter is considered local to the method,
methods in the class for the parameter are allowed to be called.
The third rule states that a method, M, can call a method, N, of an object, I,
if I is instantiated within M. This means that if a method makes a new
object, then the method is allowed to use that new object’s methods. The
object is considered local to the creating method, the same way an object is
considered local when it is a parameter.
The fourth rule states that in addition to local objects, any method, M, in
object O, can invoke methods of any type of object that is a direct
component of O. This means that a method of a class can call methods of
the classes of its instance variables. If a class has direct reference to the
Friend class, any method inside of O is allowed to call any method of Friend
class. See below for an example of this rule in Java code.
public class O {
public Friend I = new Friend();
public void M() {
this.I.N();
System.out.println("Method M invoked");
}
}
The Law of Demeter may seem complicated and abstract, but at its core,
boils down to the idea that a method should not be allowed to access
Design Patterns | 79
another method by “reaching through” an object. This means that a
method should not invoke methods of any object that is not real.
The four rules explained above help outline what is considered a local
object. These objects should be passed in through a parameter, or they
should be instantiated within a method, or they should be in instance
variables. This allows methods to directly access the behaviours of local
objects.
These conditions commonly occur when you have a chain method calls to
objects you should not know about, or when you use methods from an
“unknown” type of object that is returned back to you from your local
method call. A good metaphor for this is driving a car. When you drive a car,
you do not issue individual commands to every component of the car.
Instead, you simply tell the car to drive, and the car will know to deal with
its components.
Another way you can “reach through” an object is when the method
receives an object of an unknown type as a return value, and you make
method calls to the returned object.
The Law of Demeter defines how classes should interact with each other.
They should not have full access to the entire system because this causes a
high degree of coupling. A class should only interact with those that are
considered immediate “friends”. This prevents unwanted effects from
cascading through the entire system.
Although the Law of Demeter can help reduce coupling, you should
recognize that it also requires more time when designing and implementing
a system. It may not always be feasible to follow the Law of Demeter due to
limitations of time and resources. Other times, some degree of coupling
may be unavoidable, at which point, you’ll need to decide how much
coupling is tolerable.
No matter how well you design your code, there will still be changes that
need to be made. Refactoring helps manage this. It is the process of
making changes to your code so that the external behaviours of the code
Design Patterns | 80
are not changed, but the internal structure is improved. This is done by
making small, incremental changes to the code structure and testing
frequently to make sure these changes have not altered the behaviour of
the code.
Ideally, refactoring changes are made when features are added, and not
when the code is complete. This saves time, and makes adding features
easier.
Changes are needed in code when bad code emerges. Just like patterns
emerge in design, bad code can emerge as patterns as well. These are
known as anti-patterns or code smells.
The book Refactoring by Martin Fowler has identified many of these anti-
patterns. Code smells help “sniff out” what is bad in the code. This book
also provides refactorings in order to transform the code, and “improve the
smell”.
This next lesson will examine some of the code smells explored in Fowler’s
book. For more information on these code smells, and proposed resolutions
to them, Fowler’s book is an excellent resource. You can find bibliographic
information about the book in the Course Resources.
Comments
One of the most common examples of bad code is comments. This code
smell can occur between two extremes. If no comments are provided in the
code, it can be hard for someone else, or even the original developer
returning to the code after some time away, to understand what the code is
doing or should be doing.
On the other hand, if there are too many comments, they might get out of
sync as the code changes. Comments can be a “deodorant” for bad
smelling code. The use of a lot of comments to explain complicated design
can indicate that bad design is being covered up.
There are other ways comments can indicate bad code, though. If the
comments take on a “reminder” nature, so they indicate something that
needs to be done, or that if a change is made to one section in the code, it
needs to be updated in another method, this indicates bad code.
Comments might reveal that the programming language selected for the
software is not appropriate. This could happen if the programming
language does not support the design principles being applied. For
example, when Java was in its early years, the concept of generics did not
exist yet. Developers would use comments to explain what they were doing
Design Patterns | 81
with code when casting types. The use of comments is common in young
programming languages! However, generics have now been built into Java.
Duplicate Code
Duplicated code occurs when blocks of code exist in the design that are
similar, but have slight differences. These blocks of code appear in multiple
places in the software. This can be a problem, because if something needs
to change, then the code needs to be updated in multiple places. This
applies to adding functionalities, updating an algorithm, or fixing a bug.
Long Method
The long method anti-pattern suggests that code should not have long
methods. Long methods can indicate that the method is more complex or
has more occurring within it than it should.
Determining if a code is too long can be difficult. There is even some debate
if length of code is even a good measure of code complexity. Some
methods, such as setting up a user interface, can be naturally long, even if
focused on a specific task. Sometimes a long method is appropriate.
This anti-pattern may also depend on the programming language for the
system. For example, smalltalk methods tend to be short, with a 15 lines
average. Designs in this language tend to have highly cohesive classes and
methods, but are also highly coupled. Context is therefore important in this
anti-pattern.
DID YOU KNOW?
Some developers suggest that having an entire method visible at once on the
screen is a good guideline, with no more than around 50 lines of code.
However, some studies show that programmers can handle methods of a
Design“how
couple hundred lines before they introduce bugs! Determining Patterns | 82
long is too
long” for a method isn’t always a straightforward process!
Large Class
The large class anti-pattern suggests that classes should not be too large.
This is similar to the long pattern above.
Data Class
The data class anti-pattern is on the opposite end of the spectrum of the
large class. It occurs when there is too small of a class. These are referred to
as data classes. Data classes are classes that contain only data and no real
functionality. These classes usually have only getter and setter methods,
but not much else. This indicates that it may not be a good abstraction or a
necessary class.
An example of a data class would be a 2D point class that only has x and y
coordinates. Could something else be placed in the class? What are the
other classes manipulating this data? Would some of their behaviours be
better placed in one of the data classes? The 2D point class could have
various transformation functions that move the point. This would make
better code.
Data Clumps
Design Patterns | 83
public void doSomething (int x, int y, int z) {
...
}
private int x;
private int y;
private int z;
Be careful not to just create data classes, however. The classes should do
more than just store data. The original doSomething() method, or a useful
part it, might be added to the Point3D class to avoid this.
Design Patterns | 84
DID YOU KNOW?
When fixing code smells can lead to creating new smells elsewhere in the code!
In the example above, to fix the data clump smell, you might have accidentally
created a data class smell. Be careful when fixing code smells, and keep an eye
out for these occurrences so they can be fixed.
Another code smell is having long parameter lists. A method with a long
parameter list can be difficult to use. They increase the chance of
something going wrong. The common solution to reduce the amount of
parameters is often to have global variables. These have issues of their
own, however, and generally should be avoided.
The best solution for long parameter lists is to introduce parameter objects.
A parameter object captures context. They are common in graphics
libraries, where toolkits are passed into an object to provide context for a
graphics routine. Instead of setting details such as the pen colour, line
width, pen shape, opacity, etc. as individual parameters, this information
would be provided as a parameter object. When the program needs to
execute an method, all of the information about what the stroke should
look like can be an individual parameter value.
Divergent Class
Some code smells occur when making changes to the code itself. A
divergent change is one such code smell. It occurs when you have to
change a class in many different ways, for many different reasons. This
relates to the large class code smell, where a large class has many different
responsibilities. These responsibilities may need to be changed in a variety
of ways, for a variety of purposes. Poor separation of concerns is therefor a
common cause of divergent change.
Classes should have only one specific purpose. This reduces the number of
reasons the code would need to change, and reduce the variety of changes
Design Patterns | 85
needed to be implemented. If a class is being changed in multiple ways,
then this is a good indicator that the responsibilities of the class should be
broken up into separate classes, and responsibilities should be extracted
into their own classes.
Separation of concerns resolves two code smells – large class and divergent
change.
Shotgun Surgery
Shotgun surgery is a code smell that occurs when a change needs to be
made to one requirement, and a numerous classes all over the design need
to be touched to make that one change. In good design, a small change is
ideally localized to one or two places (although this is not always possible).
This code smell was so named, because it draws on the metaphor of a shotgun.
If you want to make a change, it should not be like taking a shotgun to your
code, and creating a large splatter effect.
This is a commonly occurring code smell. It can happen if you are trying to
add a feature, adjust code, fix bugs, or change algorithms. If changes need
to be made all over the code, then the chances of missing a change or
creating an issue elsewhere increase.
If a change in one place leads to changes in other places, then this indicates
that the methods are related in some way. Perhaps there is a better way to
organize them. If not, then you may have to deal with it as it is.
Feature Envy
Feature envy is a code smell that occurs when there is a method that is
Design Patterns | 86
more interested in the details of a class other than the one it is in. If two
methods or classes are always talking to one another and seem as if they
should be together, then chances are this is true.
Inappropriate Intimacy
If two classes are closely coupled, so a method in one class calls methods of
the other, and vice versa, then it is likely necessary to remove this cycle.
Methods should be factored out so both classes use another class. At the
very least, this makes communication one-way, and should create looser
coupling.
Cycles are not always necessarily a bad thing. Sometimes, they are
necessary. But, if there’s a way to make the design simpler and easier to
understand, then it is a good solution.
Message Chains
Message chains is a code smell that occurs when the code has long
message chains where you are calling, and get an object back, and then
calling again, and getting another object. This is a bad message chain, and
it can cause rigidity, or complexity in your design, and makes code harder
to test independently. It also potentially violates the Law of Demeter,
which specify which methods are allowed to be called.
Primitive Obsession
Primitive obsession is a code smell that occurs when you rely on the use of
built-in types too much. Built-in types, or primitives, are things like ints,
longs, floats, or strings. Although there will be need of them in the code,
they should only exist at the lowest levels of the code. Overuse of primitive
types occurs when abstractions are not identified, and suitable classes are
not defined.
Design Patterns | 87
Theoretically, almost everything in a system could be defined or encoded
as strings, and put into arrays. In fact, in the 1960s, this is how code looked.
Languages have evolved though, to allow for defining your own types for
better abstraction. Otherwise, you have a non-OO way of thinking. If
everything is stored as a string, then a key abstraction could be buried in
the detailed code, and would not be evident when looking at the design of
the system, such as through a UML diagram. There would be no clear
separation between strings.
Switch Statements
Switch statements are a code smells that occur when switch statements
are scattered throughout a program. If a switch is changed, then the others
must be found and updated as well.
Although there can be a need for long if/else statements in the code,
sometimes switch statements may be handled better. For example, if
conditionals are checking on type codes, or the types of something, then
there is a better way of handling the switch statements. It may be possible
to reduce conditionals down to a design that uses polymorphism.
Speculative Generality
The code smell speculative generality occurs when you make a superclass,
interface, or code that is not needed at the time, but that may be useful
someday. This practice introduces generality that may not actually help the
code, but “over-engineers” it.
Software changes frequently. Clients can change their mind at any time,
and drop requirements from the backlog. Your design should stay simple,
and time should not be lost on writing code that may never ben used.
Design Patterns | 88
than writing code that may not be needed down the line.
Refused Request
Code smells help identify bad code and bad design. It is good practice to
review your code frequently for code smells to ensure that the code
remains reusable, flexible, and maintainable.
This brings us to the end of this course. The next course in this
specialization is on Software Architecture.
Design Patterns | 89
Course Resources
Course Readings
Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). Design
Patterns: Elements of Reusable Object-Oriented Software. Upper
Saddle River, NJ: Addison-Wesley Professional.
Glossary
Word Definition
Design Patterns | 90
Cloning Duplication of an object.
Design Patterns | 91
Data clumps A code smell that occurs when groups of data appear
together in the instance variables of a class, or
parameters to methods.
Divergent change A code smell that occurs when you have to change a
class in many different ways, for many different
reasons.
Design Patterns | 92
Feature envy A code smell that occurs when a method is more
interested in the details of a class other than the one
it is in.
Design Patterns | 93
Large class An anti-pattern that occurs when a class is too long.
Large classes may also be referred to as God classes,
Blob classes, or Black Hole classes.
Law of Demeter A law that states that shares the principle of least
knowledge, classes should know about and interact
with as few other classes as possible, but that is
composed of several rules that provide a guideline as
to what kind of method calls a particular method can
make.
Letting the In the Factory Method pattern, the class that uses a
subclasses decide factory method is specialized or subclassed. These
subclasses must define their own factory method.
This is known as “letting the subclasses decide” how
objects are made.
Design Patterns | 94
Observer Objects in a design pattern, that rely on a subject
object to inform them of changes to the subject.
Protection proxy A proxy class used to control access to the real subject
class.
Design Patterns | 95
Refactoring The process of making changes to code so that the
external behaviours of the code are not changed, but
the internal structure is improved.
Remote proxy A proxy class that is local while the real subject class
exists remotely.
Switch statements A code smell that occurs when switch statements are
scattered throughout a program, and changing one
requires finding all of them to update.
Design Patterns | 96
Template method A behavioural design pattern that defines an
pattern algorithm’s steps generally, deferring the
implementation of some steps to subclasses. In other
words, it is concerned with the assignment of
responsibilities.
View The part of a MVC pattern that allows users to see all
or part of the model.
Design Patterns | 97