software design patterns
software design patterns
This lesson lays down the groundwork for understanding design patterns
Why Patterns ?
In the above example, we have the default constructor for the class that
takes in a single parameter the type of the aircraft. Now say after a few
days, you realize you want to add additional properties to your Aircraft
class. Say you want to add the color of the aircraft as a property, but you
have already released a version of your library and can't modify the
original constructor. The solution is to add another constructor with two
parameters like so
Aircraft(String type)
Aircraft(String type, String color)
Aircraft(String type, String color, String prop3)
Aircraft(String type, String color, String prop3, String prop4)
The telescoping pattern is called an anti-pattern: how NOT to do things!
The way to approach a class with an increasing number of variables is to
use the Builder Pattern that we'll discuss in depth in the following
chapters.
Separate out parts of code that vary or change from those that
remain the same.
This lesson lists the most used and familiar design patterns
Introduction
Design patterns for object orientated programs are divided into three
broad categories listed below. These are the same categories used by GoF
in their seminal work on design patterns.
Creational
Structural
Behavioural
Creational
Builder Pattern
Prototype Pattern
Singleton Pattern
Structural patterns are concerned with the composition of classes i.e. how
the classes are made up or constructed. These include:
Adapter Pattern
Bridge Pattern
Composite Pattern
Decorator Pattern
Facade Pattern
Flyweight Pattern
Proxy Pattern
Behavioral
Interpreter Pattern
Template Pattern
Command Pattern
Iterator Pattern
Mediator Pattern
Memento Pattern
Observer Pattern
State Pattern
Strategy Pattern
Visitor Pattern
Examples
For most of the patterns, we borrow concepts from the aviation industry
to create our examples. You'll find the course regularly talking about F-
16s and Boeings to elaborate aspects of the pattern under discussion.
For folks, who are rushing through the course for an upcoming interview,
I would suggest going through all the creational design patterns,
decorator, proxy, iterator, observer and visitor patterns. As you read
through them, be sure to look at the Java framework's api examples
pointed out in each lesson.
Builder Pattern
This lesson discusses how complex objects can be represented and constructed without coupling the two
operations.
What is it ?
Class Diagram
Builder
Concrete Builder
Director
Product
Class Diagram
Example
In our hypothetical world, every aircraft requires at least the above three
steps. However, a passenger aircraft can have an added step of making
bathrooms in the plane. The steps represent the construction process
from our formal definition. The product is an aircraft but can have
different representations such as an F-16 or a Boeing-747. Using the
same construction process, we should be able to produce both F-16s and
Boeings.
Let's see some code now. First we'll start with the abstract interface for
our AircraftBuilder class. The builder contains a method for each
component that can be part of the final product. These methods are
selectively overridden by concrete builders depending on if the builders
will be including that part in the final product variant that they are
responsible for building.
public abstract class AircraftBuilder {
Now we'll implement two concrete builders, one for F-16 and one for
Boeing-747.
Boeing747 boeing747;
@Override
public void buildCockpit() {
@Override
public void buildEngine() {
@Override
public void buildBathrooms() {
@Override
public void buildWings() {
}
F16 f16;
@Override
public void buildEngine() {
// get F-16 an engine
// f16.engine = new F16Engine();
}
@Override
public void buildWings() {
// get F-16 wings
// f16.wings = new F16Wings();
}
@Override
public void buildCockpit() {
f16 = new F16();
// get F-16 a cockpit
// f16.cockpit = new F16Cockpit();
}
For brevity's sake, we have provided the skeleton of the builders and
skipped individual implementation of each method. Note the F16Builder
doesn't override the buildBathrooms method, since there are no
bathrooms in the F-16 cockpit. The Boeing's builder does override the
bathroom's method since a Boeing-747 has bathrooms for passengers.
AircraftBuilder aircraftBuilder;
if (isPassenger)
aircraftBuilder.buildBathrooms();
}
}
Notice how we can pass in the builder of our choice, and vary the aircraft
product (representation) to be either an F-16 or a Boeing-747. In our
scenario, the builders return the same supertype however that may not
be the case if the builders return products that aren't very similar.
The AircraftBuilder interface hides how a given aircraft gets built. The
client is unaware of the classes F16Engine , F16Cockpit and similar classes
for Boeing-747.
You may find the builder pattern being used without the director. The
client can directly instantiate the builder and invoke the required
methods to get a product for itself. This is a common antidote for
telescoping constructors. Imagine a class with too many attributes but
some attributes are to be set optionally. In such a case the builder can be
invoked to only set the required attributes and create a product
Other Examples
return documentBuilder.buildDocument();
Caveats
This lesson discusses how the Singleton pattern enforces only a single instance of a class to ever get produced
and exist throughout an application's lifetime.
What is it ?
Singleton pattern as the name suggests is used to create one and only
instance of a class. There are several examples where only a single
instance of a class should exist and the constraint be enforced. Caches,
thread pools, registries are examples of objects that should only have a
single instance.
Its trivial to new-up an object of a class but how do we ensure that only
one object ever gets created? The answer is to make the constructor
private of the class we intend to define as singleton. That way, only the
members of the class can access the private constructor and no one else.
Class Diagram
Singleton
Class Diagram
Example
}
}
The above code will work hunky dory as long as the application is single
threaded. As soon as multiple threads start using the class, there's a
potential that multiple objects get created. Here's one example scenario:
Now thread B comes along and calls the getInstance method and
goes on to new-up the instance and returns the AirforceOne object.
Double-Checked Locking
return onlyInstance;
}
}
The above solution marks the singleton instance volatile however the JVM
volatile implementation for Java versions 1.4 will not work correctly for
double checked locking and you'll need to use another way to create your
singletons.
Other Examples
java.lang.Runtime
java.awt.Desktop
Caveats
This lesson discusses how new objects can be created from existing objects using the prototype pattern.
What is it ?
Imagine a class will only be loaded at runtime and you can't access
its constructor statically. The run-time environment creates an
instance of each dynamically loaded class automatically and
registers it with a prototype manager. The application can request
objects from the prototype manager which in turn can return clones
of the prototype.
Class Diagram
The class diagram consists of the following entities
Prototype
Concrete Prototype
Client
Class Diagram
Example
void fly();
IAircraftPrototype clone();
// default engine
F16Engine f16Engine = new F16Engine();
@Override
public void fly() {
System.out.println("F-16 flying...");
}
@Override
public IAircraftPrototype clone() {
// Deep clone self and return the product
return new F16();
}
// Create F16-B
IAircraftPrototype f16B = prototype.clone();
f16B.setEngine(new F16BEngine());
}
}
Dynamic Loading
Other examples
In Java the root Object class exposes a clone method. The class
implements the interface java.lang.Cloneable .
Caveats
This lesson discusses how derived classes can be given the responsibility of creating appropriate objects.
What is it ?
The problem with the above approach is that the code using the
SomeClass 's object, suddenly now becomes dependent on the concrete
implementation of SomeClass . There's nothing wrong with using new to
create objects but it comes with the baggage of tightly coupling our code
to the concrete implementation class, which is a violation of code to an
interface and not to an implementation.
Class Diagram
Product
Concrete Product
Creator
Concrete Creator
Class Diagram
Example
Continuing with our aircraft example scenario, let's assume we are trying
to model the F-16 fighter jet. The client code needs to construct the engine
object for the fighter jet and fly it. The naive implementation for the class
would be something like below:
F16Engine engine;
F16Cockpit cockpit;
switch (variant) {
case "A":
return new F16A();
case "B":
return new F16B();
default:
return new F16();
}
}
}
However, if we want to keep the creation of the F16 object parts within
the same class and still be able to introduce new F16 variants as they
come along, we could subclass F16 and delegate the creation of the right
F16 variant object to the subclass handling that variant. This is exactly the
factory method pattern! The method here is the makeF16() which we'll
make behave like a factory that produces the appropriate F16 variants.
Proceeding forward we introduce two subclasses like so
IEngine engine;
ICockpit cockpit;
@Override
public F16 makeF16() {
super.makeF16();
engine = new F16AEngine();
return this;
}
}
@Override
public F16 makeF16() {
super.makeF16();
engine = new F16BEngine();
return this;
}
}
Note that the factory method pattern, returns an abstract type, be it a Java
interface or a Java abstract class. The superclass, in our case F16 doesn't
know what variant of the F16 it was returned from the makeF16()
method. The general setup is that the superclass has the implementation
for all the methods other than the creation methods. A create method is
either abstract or comes with a default implementation and in turn is
invoked by the other methods of the superclass. The creation of the right
objects is the responsibility of the subclasses.
The factory method pattern might seem very similar to the simple or
static factory, however, the primary difference is that simple factories
can't produce varying products through inheritance as a factory method
pattern can.
Other Examples
java.util.Calendar.getInstance()
java.util.ResourceBundle.getBundle()
java.text.NumberFormat.getInstance()
Caveats
The pattern can result in too many subclasse with very minor
differences.
If the subclass extends the functionality, then the superclass can't use
it unless it downcasts it to the concrete type. The downcast may fail
at runtime.
Abstract Factory Pattern
This lesson details the working of yet another creational design pattern, that allows us to create a family of
products in a exible and extensible manner.
What is it ?
Class Diagram
Abstract Factory
Concrete Factory
Abstract Product
Concrete Product
Client
Class Diagram
Example
This innocuous looking snippet can cause severe headaches down the
road if your simulation software takes off and you need to expand it to
other aircraft. Below is a list of what is wrong with the above snippet:
The concrete classes for the three parts have been directly exposed to
the consumer.
F-16 has several variants with different engines and say in future if
you want to return engine object matching to the variant, you'll need
to subclass F16Engine class and that would necessitate a change in
the consumer snippet too.
We'll fix these issues one by one and see how the abstract factory pattern
would emerge.
void start();
}
@Override
public void start() {
System.out.println("F16 engine on");
}
}
With the above change see how the corresponding consumer code
changes
Creating a factory
F16 aircraft and deliver them to the requesting client. The class would
take the following shape.
Note how this setup allows us the freedom to change the concrete class
representing the F16Engine as long as it commits to the IEngine interface.
We can rename, enhance or modify our class without causing a breaking
change in the client. Also note that by just differing the factory class
passed into the client constructor, we are able to provide the client with
the same parts for a completely new aircraft. This is discussed next.
Factory of Factories
Wouldn't it be great if we could use the same client snippet for other
aircraft such as Boeing747 or a Russian MiG-29? If we could have all the
factories being passed into the client agree to implement the
createEngine() method, then the client code will keep working for all
kinds of aircraft factories. But all the factories must commit to a common
interface whose methods they'll implement and this common interface
will be the abstract factory.
Implementation
Let's start with an interface that would define the methods the factories
for different aircraft would need to implement. The client code is written
against the abstract factory but composed at runtime with a concrete
factory.
IEngine createEngine();
IWings createWings();
ICockpit createCockpit();
}
Note that we mean a Java abstract class or a Java interface when referring
to "interface". In this instance, we could have used an abstract class if
there were a default implementation for any of the products. The create
methods don't return concrete products rather interfaces to decouple the
factory consumers from the concrete implementation of parts.
The formal definition of the abstract factory pattern says abstract factory
pattern defines an interface for creating families of related products
without specifying the concrete classes. Here the IAircraftFactory is that
interface in the formal definition and note how its create methods are not
returning concrete parts but rather interfaces that'll be implemented by
the concrete parts' classes.
@Override
public IEngine createEngine() {
return new F16Engine();
}
@Override
public IWings createWings() {
return new F16Wings();
}
@Override
public ICockpit createCockpit() {
return new F16Cockpit();
}
}
@Override
public IEngine createEngine() {
return new Boeing747Engine();
}
@Override
public IWings createWings() {
return new Boeing747Wings();
}
@Override
public ICockpit createCockpit() {
return new Boeing747Cockpit();
}
}
interface that we don't list for brevity's sake. The interfaces representing
the parts would be:
IEngine
ICockpit
IWings
All the create methods are actually factory methods that have been
overridden. Indeed, the factory method pattern is utilized when
implementing the abstract factory pattern. For the sake of brevity, we
have skipped listing the concrete classes for engine, wings, and cockpit.
In the previous lesson, we created a class for F-16 which included a public
method fly() . This method internally invoked the makeF16() method and
after the aircraft was manufactured, it invoked the taxi() method before
printing a fly statement. In our scenario, all aircrafts are expected to
follow the same pattern. They first get manufactured, then taxi on the
runway and then fly away. We can thus create a class for an aircraft that
does these three tasks. Note, how we aren't creating separate classes to
represent the two aircraft i.e. the F-16 and Boeing-747 rather a single
Aircraft class that can represent both.
IEngine engine;
ICockpit cockpit;
IWings wings;
For now we'll keep the makeAircraft method empty. Let's first see how a
client will request F-16 and Boeing-747 objects.
}
}
We'll need to add a constructor to our Aircraft class, which will store the
passed-in factory object and create the aircraft parts using the factory.
Just by composing the aircraft object with a different factory we are
able to get a different aircraft. The complete version of the aircraft class
would look like below:
IEngine engine;
ICockpit cockpit;
IWings wings;
IAircraftFactory factory;
The client just needs to instantiate the right factory and pass it in. The
consumer or client of the factory is the Aircraft class. We could have
created an interface IAircraft to represent all the aircraft that the class
Aircraft in turn would implement but for our limited example it's not
necessary.
Other Examples
Caveats
It might appear to the naive reader that the factory method pattern
and the abstract factory pattern are similar. The difference between
the two lies in their motivations. The factory method pattern is
usually responsible for creating a single product whereas an abstract
Adapter pattern is similar to how an electrical adapter lets your laptop work both in the US or UK even though
voltages are different.
What is it ?
When two heads of states who don't speak a common language meet,
usually a language interpreter sits between the two and translates the
conversation, thus enabling communication. The Adapter pattern is
similar in that it sits between two incompatible classes that otherwise
can't work with eachother and lets them work together. Another example
to consider is when one buys electronics from USA and tries to use them
in India. The two countries have different power voltages being
distributed to consumers and using an electronic appliance from one
country in another requires a physical adapter which steps up or down
the voltage appropriately. The concept of the software adapter pattern is
similar.
Class Diagram
Target
Client
Adaptee
Adapter
Class Diagram
Example
Let's take our aircraft example again. Your software only deals with fancy
jets but suddenly you are required to adapt your software to cater to a
local hot air balloon company. Rewriting your software from scratch is
not feasible. To complicate matters the balloon company already provides
you with classes that represent hot air balloons which are incompatible
with your IAircraft interface, which you use to represent modern
aircraft. We'll use the adapter pattern to make the hot air balloon classes
work with our existing infrastructure for aircraft. Let's see what the
balloon class looks like:
}
}
HotAirBalloon hotAirBalloon;
@Override
public void fly() {
String feulUsed = hotAirBalloon.inflateWithGas();
hotAirBalloon.fly(feulUsed);
}
}
The adapter is composed with the Adaptee object, which in our case
is the HotAirBalloon object.
The adapter implements the interface the client knows about and
consumes. In this case, it is the IAircraft .
hotAirBalloonAdapter.fly();
}
Object Adapter
The hot air balloon example that we just discussed is really an object
adapter example. We composed the adapter with the adaptee object to
make incompatible classes work together. In the case of Java, we can only
practice object adaptation for reasons you'll learn shortly.
Class Adapter
Other Examples
If you have two applications, one spits out output as XML and the
other takes in input as JSON then you'll need an adapter between the
two to make them work seamlessly.
This lesson discusses how parallel class hierarchies or layers can be decoupled from one another using the bridge
pattern.
What is it ?
The bridge pattern can be applied to scenarios where the class and what
it does changes often. Think of it as two layers of abstraction. The class
itself becomes one layer and what it does i.e. the implementation
becomes another layer. This setup allows us to extend the two layers
independently of each other. In Java, both the layers would be
represented by two separate class hierarchies. The bridge sits between
these two class hierarchies, allowing the class abstraction to configure
itself with the implementation abstraction.
Class Diagram
Refined Abstraction
Implementor
Concrete Implementor
Class Diagram
Example
Suppose you are writing software for Toyota Motors and need to
represent the most sold car in the world - the Toyota Corolla. We'll use an
abstract class Corolla to represent the car. The concrete classes would
represent each of the different models of the car. So far so good.
However, the same model could be built to different standards for
different locales. For instance, the North American model may have
different safety requirements than an Asian model. The same model
could be left or right handed depending on which country it is being
shipped to. Corolla has several models and the models have different
names in different countries. In the US, the different models include L, LE,
XLE etc. For our purposes we'll consider only one model L.
Model L Model SE
We can divide the above class structure into two hierarchies. One that just
represents the models of the car and another that represents the location-
specific variations for each model of the car. After applying the pattern
the class diagram would look like below:
Corolla CorollaImpl
So you can see there's one hierarchy of class the Corolla which would
have subclasses for each of the models, however, the actual car produced
by the factory may be different from another car of the same model that
is destined for a different country. The implementation of the car would
make up a separate class hierarchy CorollaImpl which will have
implementation subclasses for each of the Corolla models and have
information such as safety equipment installed, whether the car is left or
right handed drive etc.
You would have guessed by now that the class Corolla (the abstraction)
would hold a reference to an object of the class CorollaImpl (the
implementation) and invoke method calls on the implementation object.
We are using object composition to add location dependent behavior to
each model of the car.
Let's examine the first class hierarchy that represents the car Corolla and
acts as the abstraction.
@Override
void listSafetyEquipment() {
corollaImpl.listSafetyEquipment();
@Override
boolean isCarRightHanded() {
return corollaImpl.isCarRightHanded();
}
}
@Override
void listSafetyEquipment() {
System.out.println("Not so safe.");
}
@Override
boolean isCarRightHanded() {
return false;
}
}
@Override
void listSafetyEquipment() {
System.out.println("High safety standards.");
}
@Override
boolean isCarRightHanded() {
return true;
}
}
The client can use the classes like so:
myCorolla.setCorollaImpl(new Corolla_L_Impl_NorthAmerica());
System.out.println(myCorolla.isCarRightHanded());
}
}
Note how the client can switch out the implementation class object at
runtime and make the model behave for an entirely different location.
Using the bridge pattern we have avoided permanent binding of models
and their intended locations of operation. The client can continue to work
with the objects of the abstraction layer without noticing any changes to
implementation layer classes. New safety rules or regulations would only
affect the implementation layer classes.
Other Examples
Consider the development of a GUI toolkit. The toolkit will likely use
system level api calls specific to the operating system. If you design a
widget menu then you'll need to subclass the menu for both
Windows and Linux. If you have several dozen widgets then each
widget class will end up with subclasses for each operating system
you intend to target. The code slowly becomes one giant monolith
that is hard to change or debug.
Menu
Caveats
The bridge pattern may be confused with the adapter pattern but one
difference between the two is that the adapter pattern is usually
applied after a system is designed whereas the bridge pattern is
intentionally applied as part of the design process to decouple the
two layers.
Composite Pattern
This lesson discusses the composite pattern that lets us treat individual elements and group of elements as one.
What is it ?
The pattern allows the clients to ignore the differences between the whole
and the part.
Class Diagram
Component
Leaf
Composite
Client
Class Diagram
Example
Assume, that we now want to represent all the aircraft in the combined
air forces of the NATO alliance. An air force is primarily made up of
several aircraft but it can also have sub-air forces. For instance, the US
has the 1st Air Force, 2nd Air Force so on and so forth. Our NATO alliance
can consist of air forces from multiple countries including individual
planes.
If we want to treat the composite and each part as the same, we would
need both the part (the aircraft) and the whole (the airforce) to implement
the same interface. In our scenario we'll create three classes:
Airforce
F16
C130Hercules
NATO
Airforce
US Canadian
C-130 Airforce Airforce
The class Airforce will represent the composite and the other two
classes the part. Furthermore, we'll create an interface IAlliancePart
that will allow us to treat the objects from each of the three classes as one
type.
@Override
public int getPersonnel() {
// We need 2 pilots for F-16
return 2;
}
}
@Override
public int getPersonnel() {
// This cargo plane, needs 5 people
return 5;
}
}
The above two classes act as parts, now we'll write the composite class
Airforce .
@Override
public int getPersonnel() {
while (itr.hasNext()) {
staff += itr.next().getPersonnel();
}
return staff;
}
}
The internal iterator will recursively call the getPersonnel method on the
nested air force objects. The leaves would actually be the planes and will
return a number. The personnel count for the root air force object will be
the sum of all the people required to operate all the planes.
The client can invoke the getPersonnel method on the root object and get
a total count. Note how transparency is created by treating the composite
and the part as same. The client code or the internal iterator code doesn't
need conditional if-else statements to check for the type of the object and
then call the appropriate method on it. The client code appears below:
NatoAllaiance.add(frenchF16);
NatoAllaiance.add(germanCargo);
System.out.println(NatoAllaiance.getPersonnel());
}
}
Other Examples
Caveats
references to parents: Since the composite is a tree structure, one
may or may not need to store references to the parent.
This lesson discusses how objects can be enhanced with new behavior using the decorator pattern.
What is it ?
The strategy is to wrap the existing object within a decorator object that
usually implements the same interface as the wrapped object. This allows
the decorator to invoke the methods on the wrapped object and then add
any additional behavior. Usually, the decorator adds behavior to the
existing functionality of the wrapped object i.e. the decorator takes action
either before or after invoking some method on the wrapped object.
Class Diagram
Component
Concrete Component
Decorator
Concerete Decorator
Class Diagram
Examples
Say we are interested in the weight of our plane, which can be important
in determining the fuel required for flights. Adding either or both of the
options would make the plane heavier. We would want an extensible way
of adding properties to the plane object and still be able to know its
weight with the additional packages installed on the plane.
Let's first see how the aircraft interfaces and classes look like:
void fly();
void land();
float getWeight();
@Override
public void fly() {
System.out.println("Boeing-747 flying ...");
}
@Override
public void land() {
System.out.println("Boeing-747 landing ...");
}
@Override
public float getWeight() {
return baseWeight;
}
}
We'll have two concrete decorators, one for the luxury fittings and the
other for bullet proofing the plane.
IAircraft boeing;
@Override
public void fly() {
boeing.fly();
}
@Override
public void land() {
boeing.land();
}
@Override
public float getWeight() {
return (30.5f + boeing.getWeight());
}
}
IAircraft boeing;
@Override
public void fly() {
boeing.fly();
}
@Override
public void land() {
boeing.land();
}
@Override
public float getWeight() {
return 50f + boeing.getWeight();
}
}
See how the concrete decorators save a reference to the object that they
wrap. The getWeight method in each decorator calls the base model s
getWeight to get the base model's weight and then adds the weight added
to the plane because of itself. The client can use the decorators like so:
From the client code, one can observe how the plane's behavior is
extended at runtime. Note that the decorator's abstract class implements
the same interface as the Boeing747. This is so that the concrete decorator
object can stand in place of the Boeing object. From the client code, one
can see how we wrap the boeing object in successive decorators and are
able to retrieve the net weight.
Other Examples
bufferedInputStream.read();
}
Caveats
One of the issues with the decorator pattern is that we may end up
with too many classes as the number of decorators grows. The
java.io package suffers from the same issue, as it makes extensive
use of the decorator pattern.
This lesson discusses how the interface to a complex system of interacting entities can be simpli ed by providing
a front that hides the subsystem intricacies from the client.
What is it ?
If you take a look around the amenities of current life, almost everything
is a facade. When you press a button to turn on the room lights. The
button is a facade that hides from you the complexities of electric power
generation and distribution and magically lights up your room. The
facade makes complex systems easier to use.
Class Diagram
Facade
Subsystem Classes
Class Diagram
Example
Modern aircrafts have a feature called autopilot that allows the airplane
to fly to its destination in an automated fashion without much
interference from human pilots. The autopilot feature needs to juggle
with all the subsystems and health-checks of the aircraft to ensure a
smooth flight. It can hide away all the underlying complexity of
automated flight from a pilot. Let's look at how the Boeing autopilot
facade would be created:
engineController.setEngineSpeed(700);
navigationSystem.setDirectionBasedOnSpeedAndFeul(
engineController.getEngineSpeed(),
feulMonitor.getRemainingFeulInGallons());
}
You may argue that the class Boeing747 could have just as well invoked
the required methods on the subsystem objects and you are right. The
intent is not to hide the subsystems but to make it easier to use the
collection of subsystems. The Boeing747 class only works with the facade.
It codes against the facade rather than individual subsystems so that
tomorrow if any subsystem is switched out for a better one the change is
quarantined to the facade and doesn't cascade across the code base.
The facade pattern shields the client from having to deal with all the
complex subsystem classes, thus creating a loose coupling between the
subsystem and its clients. Upon receiving a request, the facade forwards
the request to the appropriate subsystem and may do any necessary
translation inbetween.
Other Examples
Caveats
This lesson discusses how the yweight pattern can be applied to reduce memory requirements.
What is it ?
Class Diagram
Flyweight
Concrete Flyweight
Flyweight Factory
Client
Example
Following OO principles to the core may lead you to create too many
objects in your application that have part of their state shared. For
instance, continuing with our aircraft scenario, if you are designing a
global radar that tracks all the planes currently airborne in the world at
any time then your radar screen will show thousands of airplanes
represented as objects in memory. If your hardware is limited in memory
then you have a problem.
Each object would have some shared state that is independent of where
the plane is flying in the world. This state which is independent of the
context of the plane is called intrinsic state and can be factored out and
shared amongst all similar planes. The state of the plane which changes
with the context is called the extrinsic state. In this case, the coordinates
of the plane will change for each plane and can be thought of as the
extrinsic state. The remaining amount of fuel for each airplane is another
piece of information that is extrinsic. However, the number of crew
required to fly a particular variant of the F-16 would be the same across
all the F-16s of that variant that are airborne. This would be an example
of intrinsic state. The crew number isn't dependent on the context, i.e.
which part of the world is the plane flying in, which country does it
Using the flyweight pattern, we can move the extrinsic state of the object
outside of the class and only keep the intrinsic state within the class. This
change would allow us to reuse the same F-16 object for all the F-16s that
are currently airborne and show up on the radar. The number of objects
required to represent the flying F-16s would drastically reduce. The
extrinsic state of the planes can always be passed-in to the methods that
use it.
Let's see how the F-16 flyweight class would look like
// Intrinsic state
private final String name = "F16";
private final int personnel = 2;
private final String dimensions = "15m long 3m wide";
private final String wingspan = "33 feet";
// Extrinsic state includes the current position and current spee
d
return 1;
}
}
The client code can take advantage of the flyweight like so:
}
}
Note, how the client is receiving the extrinsic state for each of the F-16 in
a two-dimensional array. The flyweight F16 class has information specific
to a F-16 plane that won't change. For brevity's sake the getters for the
private fields are skipped.
Other Examples
Caveats
This lesson discusses how objects can act on behalf of other objects without the clients knowing they aren't
talking to the intended object.
What is it ?
There are a number of other use cases such as the firewall proxy,
synchronization proxy etc.
Class Diagram
The class diagram consists of the following entities
Proxy
Subject
Real Subject
Class Diagram
Remote Proxy
The subject doesn't have the intelligence built into it to receive the
method invocation request over the network and usually, a helper entity
runs alongside the remote subject and handles network communication
on behalf of the subject. Once the method invocation request is received,
the helper entity forwards it to the subject, which in turn executes the
method with the passed-in arguments. The results are then ferried back
over the network to the proxy. The proxy in turn returns the results to the
client.
Virtual Proxy
Protection Proxy
Example
We'll show a simple example of a remote proxy.
Consider a drone that is being flown by a pilot on the ground. The drone
can be thought of as the subject, while the ground cockpit can consist of a
proxy that receives actions from the physical controls in the cockpit and
forwards them to the remote drone's software.
The DroneProxy and the actual subject Drone will both implement the
same interface IDrone . The client will talk with the drone proxy without
knowing that it is not talking to the real subject. The drone proxy would
in turn forward requests from the client to the real drone object running
in the flying drone's computer memory over a wireless connection. The
action taken on the ground by the pilot against the drone proxy will be
mimicked by the flying drone.
void turnLeft();
void turnRight();
void firstMissile();
}
@Override
public void turnLeft() {
// forward request to the real drone to
// turn left over the internet
}
@Override
public void turnRight() {
// forward request to the real drone to
// turn right over the internet
}
@Override
public void firstMissile() {
// forward request to the real drone to
// fire missile
}
}
switch (action) {
case "left": {
droneProxy.turnLeft();
break;
}
case "right": {
droneProxy.turnRight();
break;
}
case "fire": {
droneProxy.firstMissile();
break;
}
default:
System.out.println("Invalid Action");
}
}
}
}
The object of class Drone which will be part of the code that runs on the
hardware of the flying drone and controls its flight may look like below:
public class Drone implements IDrone {
@Override
public void turnLeft() {
// receives the request and any method parameters
// over the internet to turn the drone to its left.
}
@Override
public void turnRight() {
// receives the request and any method parameters
// over the internet to turn the drone to its right.
}
@Override
public void firstMissile() {
// receives the request and any method parameters
// over the internet to fire a missile
}
}
Other Examples
Caveats
A proxy may also be responsibile for additional house-keeping tasks
such as deleting the subject, keeping a reference count to the real
subject or encoding requests before sending them to the real subject
over the wire.
Chain of Responsibility Pattern
This lesson discusses how a request can travel down a chain of handlers till an appropriate handler is found.
What is it ?
The requestor has no knowledge of the object that will eventually handle
its request nor does it have a reference to the handling object. Similarly,
the object eventually handling the request isn't aware of the requestor.
Each object in the chain should implement a common supertype and have
a reference to its successor. The handler objects can be added to or
removed from the chain at runtime.
Class Diagram
Concrete Handler
Client
Example
super(2);
}
}
@Override
public void handleRequest(AbstractRequest request) {
if (code == request.getRequestCode()) {
// Handle the request here.
} else {
// If the handler, doesn't handle these type of
// requests, it can just call the super class's
// forward request method.
super.handleRequest(request);
}
}
}
Notice, how in our example, the request moves along the chain till a
handler that is capable of addressing the request receives it. The chain
also defines an order in which the request gets handled. If more than one
handler can serve the request, then it'll get handled by the one occurring
first in the chain.
Other Examples
This lesson discusses how updates from an object of interest can be communicated ef ciently to interested parties
using the observer pattern.
What is it ?
Class Diagram
Subject
Observer
Concrete Subject
Concrete Observer
<<Interface>> <<Interface>>
ISubject IObserver
+ update(Object)
+ addObserver(IObserver)
+ removeObserver(IObserver)
+ notifyObservers()
Extends Extends
ControlTower F16
concrete class concrete class
To ensure loose coupling we'll define an interface for the subject and one
for the observer.
public interface ISubject {
void notifyObservers();
}
Example
Going back to our aircraft example, we can say that any aircraft in flight
would be interested in updates from the air-traffic controller. We can
imagine that an aircraft, as soon as, it is airborne would want to subscribe
to updates from the air traffic controller and unsubscribe when it lands.
Publisher Code
We create a control tower class which acts as a publisher for all aircraft.
@Override
public void addObserver(IObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(IObserver observer) {
// Logic to remove the observer goes in here
}
@Override
public void notifyObservers() {
for (IObserver observer : observers) {
// We are passing null for state here but we
// could pass 'this' the subject itself or a
// type representing the state. These two options
// represent the Pull vs Push models
observer.update( null);
}
}
/**
* This is hypothetical function that runs perptually, gathering
* runway and weather conditions and notifying all observers of
* them periodically.
*/
public void run() {
while (true) {
// get new runway/weather conditions and update observers
// every five minutes
// Thread.sleep(1000 * 60 * 5)
notifyObservers();
}
}
}
Observer Code
The F-16 class would implement the IObservable as objects of the F-16
class would want updates from the ControlTower class.
ISubject observable;
public F16(ISubject observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void fly() {
System.out.println("F16 is flying ...");
@Override
public void land() {
@Override
public void update(Object newState) {
// Take appropriate action based on newState
}
}
Push vs Pull
Note how the F-16 class receives the new state as a type of class Object .
We can pass in a more specific type if we agree on what information gets
passed. The way the code is structured, it represents the Push Model
where the subject is responsible for pushing the new state. Say if the
aircraft is a helicopter, is it really interested in the runway conditions? It's
supposed to land on a helipad and may not use all the information that it
gets passed for the runway.
Caveats
Some issues one needs to keep in mind while working with the observer
pattern.
In case of many subjects and few observers, if each subject stores its
observers separately, it'll increase the storage costs as some subjects
will be storing the same observer multiple times.
This lesson delves into the interpreter pattern, which allows us to simplify representation and implementation of a
new programming language albeit with limited syntax.
What is it ?
Grammar
Regular
Context Free
Context Sensitive
Recursively Enumerable
Context Free Grammar
start symbol
The above rules say that the left hand side expression, which is a non-
terminal symbol, can be expanded into the values on the right hand side.
A non-terminal symbol is nothing but a variable or a placeholder which
can be expanded into the right hand side values. One can recursively
keep expanding the non-terminal symbols till a terminal symbol is
reached. This is similar to how a recursive algorithm stops recursion once
it reaches the base case, otherwise the program would continue in an
infinite loop.
The arithematic CFG will have <expression> as the start symbol and + - *
/ number will form the set of terminals, where number is any valid
number.
Connecting back
With the above discussion, now we are in a better position to define the
interpreter pattern. The Interpreter pattern uses a class to represent each
grammar rule. Symbols on the right-hand side of the rule are instance
variables of these classes.
Class Diagram
Abstract Expression
Terminal Expression
Nonterminal Expression
Context
Client
Class Diagram
Example
Let's say you are writing an educational programming language for kids
who aspire to be pilots someday. Your language would be very simple and
will allow kids to control a plane object on-screen using the following
keywords, which make up your programming language:
Glide
SplitS
BarrelRoll
The plane object on the screen will perform one of the three actions when
reading the program script. However the restriction is that a plane must
start and end with a glide operation and can't perform stunts
consecutively, i.e. the splitS and barrelRoll must be separated by a glide
operation.
The above language can be defined by the grammar below:
Flight is the start symbol and represents the program a child wrties.
1. <Flight>
The recursion ends with the terminal symbols. Applying the interpreter
pattern, we model each of the grammar rules as a class. Symbols on the
right-hand side of the rule are instance variables of these classes.
The class diagram for the language defined by the grammar would look
like below:
Program
Interpret()
Flight ShowOff
Interpret()
Interpret()
Abstract syntax trees (AST) are widely used in compilers to represent the
structure of a program. Don't confuse the abstract to mean an abstract
class or interface. Without getting into too much detail, the takeaway is
that all the language strings produced by a grammar can be represented
as an abstract syntax tree. The tree nodes would be the classes we created
from the grammar rules. The internal nodes will be non-terminal symbols
and the leaves must necessarily be terminal symbols. Lets take the string
we generated earlier glide splitS glide barelRoll glide and see how its
AST would look like.
Flight
Flight1
ShowOff
Flight2
glide splitS
The classes Glide , BarrelRoll and SplitS will check to see if the
input matches any of those words.
The class ShowOff will check if the input matches either of the two
values it can take on.
The class Flight class will check if the input matches the terminal
symbol or expands into another concatenation of non-terminal
symbols.
We'll skip the implementation that'll contain the logic for matching the
input stream as its not necessary to understand the pattern.
Flight flight;
ShowOff showOff;
Glide glide;
@Override
public void interpret(Context context) {}
}
BarrelRoll barrelRoll;
SplitS splitS;
@Override
public void interpret(Context context) {
}
}
@Override
public void interpret(Context context) {
}
}
}
}
@Override
public void interpret(Context context) {
}
}
while (ast.hasNext()) {
Program node = ast.getNextNode();
node.interpret(context);
}
}
}
Other Examples
This lesson discusses how actions and requests can be encapsulated as objects to act as callbacks and in the
process allow logging, queueing and undo of commands.
What is it ?
Class Diagram
Concrete Command
Client
Invoker
Receiver
Class Diagram
Example
Going back to our aircraft example, imagine the cockpit of the Boeing-747.
It has a multitude of instrument panels with knobs and buttons. For
simplicity's sake let's say the plane has a button for the landing gear (the
wheels of the aircraft), which allows the landing gear to be lowered or
retracted. The button shouldn't need to know how the landing gear
works, it just needs to know who has the knowledge to operate the
landing gear. The who part will implement the Command interface and the
button will know it needs to invoke the execute method on the who
object.
@Override
public void execute() {
landingGear.up();
}
}
public InstrumentPanel() {
}
Notice how the Invoker is simply setting up the commands and then
invoking the execute method on the command objects. We can very well
replace the command object with an instance of a different
implementation and the invoker would still work correctly. This allows
decoupling between the invoker and the receivers. The Command pattern
decouples the object that invokes the operation from the one having the
knowledge to perform it.
The last piece to the command pattern is the client which sets up the
invoker with the right commands and the commands with the right
receiver objects.
Macro Command
Other Examples
Caveats
can then be stored in a list and traversing the list forwards and
backwards while invoking execute or unexecute can support redo
and undo respectively. The memento pattern can be helpful in storing
the state a command needs to undo its effects.
The command interface can add methods to save and read from disk
allowing logging of commands. In case of a crash the log can be read
and the commands re-executed in the same sequence to get the
system back to the state just before the crash.
This lesson discusses how the underlying elements that make up an aggregate object can be looped through
without exposing implementation details to clients.
What is it ?
Iterator<String> it = companiesIWantToInterviewFor.iterator
();
while (it.hasNext()) {
System.out.println(it.next());
}
Class Diagram
Iterator
Concrete Iterator
Aggregate
Concrete Aggregate
Example
Java already has an interface Iterator but for the purposes of learning,
we'll create it from scratch. Our interface will expose only two methods
next() and hasNext() . It'll look something like below:
void next();
boolean hasNext();
}
public AirForce() {
jets.add(new F16());
helis[0] = new CobraGunship();
cargo.add(new Boeing747());
}
}
}
Note the methods createIterator() and createJetsIterator() return
objects that implement the Iterator interface. Notice how each kind of
aircraft is stored in a different type of collection. If a client needs to list all
the aircraft in an airforce object, it'll have a hard time invoking getters
and then going over each individual collection. We mask this complexity
by creating an iterator class whose sole job is to list all the aircraft held by
the airforce. Look at the implementation below:
List<IAircraft> jets;
IAircraft[] helis;
LinkedList<Boeing747> cargo;
int jetsPosition = 0;
int helisPosition = 0;
int cargoPosition = 0;
/**
* The iterator is composed with the object it'll be iterating ov
er
*/
public AirForceIterator(AirForce airForce) {
jets = airForce.getJets();
helis = airForce.getHelis();
cargo = airForce.getCargo();
@Override
/**
* We provide our own custom logic of returning aircraft in a
* sequence. Note we are returning IAircraft interface object whi
ch
* every plane in our airforce implements. We also design the fun
ction
* to throw a runtime exception if next is invoked when no more e
lements
* are left to iterate over
*/
public IAircraft next() {
@Override
public boolean hasNext() {
while (jets.hasNext()) {
jets.next();
}
while (allPlanes.hasNext()) {
allPlanes.next();
}
}
Notice, the client has no idea about how the different airplane types are
held in the airforce object nor does it know what type of aircraft they are.
It simply gets to work with the IAircraft interface.
The Java API has its own iterator interface which also includes remove()
and forEachRemaining() methods that we have not included in our in-
house iterator.
When the iteration control rests with the client using the iterator, that is,
the client is responsible for advancing the traversal and explicitly
requesting the next element from the iterator, it is an example of an
external iterator. Iterators in our aircraft example are external
iterators. On the other hand, when the client hands the iterator an
operation to perform and the iterator performs the operation on each
element of the aggregate, it is an example of an internal iterator.
Other Examples
Caveats
Note that there could be more than one pending traversals on the
aggregate. Each iterator would store its own traversal state. The
iterator can apply the memento pattern to store the traversal state.
This lesson discusses how the mediator pattern centralizes interaction amongst a number of interacting objects
reducing coupling and dependence among them.
What is it ?
The mediator pattern should be applied when there are many objects
communicating in a well-structured but complex manner resulting in
interdependencies that are hard to understand. The participant objects in
such a scheme don't lend themselves for reuse because of dependence on
so many other objects.
Class Diagram
Mediator
Concrete Mediator
Colleague Classes
Class Diagram
Example
// perpetual loop
while (true) {
IAircraft aircraft;
synchronized (this) {
aircraft = queuedForLanding.remove(0);
}
// We have only one runway available so only allow a sing
le
// aircraft to land.
aircraft.land();
}
}
}
@Override
public void land() {
System.out.println("F16 landing...");
}
}
}
We used the control tower example also for the observer pattern and it
applies in the mediator pattern too as the interaction between the
mediator and the colleagues can be modeled on the observer pattern.
Other Examples
This lesson discusses how the state of an object can be exported as a snapshot without exposing the internals of
the object.
What is it ?
Details
Memento lets the originator entrust other objects with information it'll
need to revert to a previous state without exposing its internal structure
or representations.
Class Diagram
Memento
Originator
Caretaeker
Class Diagram
Example
ObjectInputStream objectInputStream
= new ObjectInputStream(bis);
BlackBox blackBox = (BlackBox) objectInputStream.readObject
();
objectInputStream.close();
return blackBox;
}
In Java, we can use serialization to save the state of the object. We are
simply required to mark our class with the Serializable interface, which
has no methods to implement. Note that in our setState method, we are
returning an object of type BlackBox that the client can then assign. In
Java, we can't assign to the this keyword, whereas in C++ we can. If it
were a C++ implementation, we could have simply assigned the this
variable the deserialized object, instead of returning it.
// Do some work.
Other Examples
Caveats
loading the right memento. This allows the game's codebase to free
itself of managing game states per user.
This lesson discusses how an object exhibits very different behavior and appears to be an instance of a different
class when its internal state changes under the state pattern.
What is it ?
Class Diagram
Context
State
Let's take the case of our F-16 class. An instance of the class can be in
various states. Some possible states and transitions to other states are
listed below:
Land Taxi
pilotParks
Parked Taxi
pilotEjects
pilotFlies
pilotParks
Crash
pilotEjects pilotEjects
Airborne Airborne
pilotLands
Land Airborne
The verbs in red in the state diagram are actions that propel the aircraft
into different states.
Let's see how we'll write the code in the absence of the state pattern. We'll
code the state transition function when the pilot takes the taxi action.
if (currentState == "Parked") {
currentState = "Taxi";
System.out.println("Plane is taxing on the runway.");
The method pilotTaxies captures the state transitions once the taxi
action is taken by the pilot. One can see the labyrinth of if-else conditions
will only grow bigger, once we start adding more states for the plane.
Now let's see how the state pattern fixes the above problem. The state
pattern will create classes for each of the possible states and each class
implements the state-specific behavior. This will result in more number
of classes but the design will become flexible and extensible as you'll
shortly see. First let's capture the actions a pilot can take, in an interface,
which is the State Interface. This interface would then be implemented
by the different states a F-16 can be in.
/**
* This interface defines the actions a pilot
* can take against the aircraft object. Each
* action will move the aircraft into a different
* state
*/
public interface IPilotActions {
Now let's see how a state would implement this interface. Let's start with
the ParkedState . For brevity, we'll only show the parked state class. Each
of the other states will have a corresponding class that'll code behavior
for the F-16 in that state.
F16 f16;
// Notice, how the state class is composed with the context objec
t
public ParkedState(F16 f16) {
this.f16 = f16;
}
@Override
public void pilotTaxies(F16 f16) {
f16.setState(f16.getTaxiState());
}
@Override
public void pilotFlies(F16 f16) {
System.out.println("This is an invalid operation for this sta
te");
}
@Override
public void pilotEjects(F16 f16) {
f16.setState(f16.getCrashState());
@Override
public void pilotLands(F16 f16) {
System.out.println("This is an invalid operation for this sta
te");
}
@Override
public void pilotParks(F16 f16) {
System.out.println("This is an invalid operation for this sta
te");
}
}
Note how the constructor accepts an instance of the context and saves a
reference to it. The plane can only transition to TaxiState or
CrashedState from the ParkedState. Either the pilot successfully revs up
the engine and takes the plane on the runway or he presses eject if say a
fire breaks out on ignition. In our state transition model, you can see that
CrashedState is a terminal state and there are no transitions out of this
state. One can't park, fly, taxi, land or crash again a crashed plane.
The client will use our new set of classes like so:
}
}
For completeness, the F16 class and the associated interface appear
below:
public F16() {
state = parkedState;
}
void startsEngine() {
state.pilotTaxies(this);
}
void fliesPlane() {
state.pilotFlies(this);
}
void landsPlane() {
state.pilotLands(this);
}
void ejectsPlane() {
state.pilotEjects(this);
}
void parksPlane() {
state.pilotParks(this);
}
ParkedState getParkedState() {
return parkedState;
}
CrashState getCrashState() {
return crashState;
}
LandState getLandState() {
return landState;
}
TaxiState getTaxiState() {
return taxiState;
}
public AirborneState getAirborneState() {
return airborneState;
We have delegated the transitions to the state classes. They decide what
would be the next state depending on the action the pilot takes.
This pattern may seem very similar to the Strategy Pattern, however, the
intent of the two patterns is very different. In strategy, the client is
actively composing the context with the strategy object whereas, in the
state pattern, the client has no view of the state the context is currently in.
The context may show a different behavior for being in a different state.
It might appear to the client that the context is a different class altogether
when in fact it's only in a different state.
For our example when the F-16 is airborne it becomes capable of using its
weapons, firing missiles, destroying tanks etc, all of which it can't do in
the parked state. The alteration in the behavior that comes with the state
change, almost makes the object appear to belong to a different class.
Other Examples
Caveats
In our example, the F 16 object is assumed to be in parked state when
new-ed up however a client can configure the context with a state at
instantiation time if the context can be in more than one states when
new-ed up. But once configured, the clients don't deal with context
states again.
If the state types only contain behavior and no data then their
instances can be expressed as flyweight objects and shared amongst
multiple context objects. In our example the ParkedState only
contains methods and no data particular to an object of F16. This
allows us to have multiple F16 objects use the same parked state
object to be in the parked state.
Template Method
This lesson discusses how algorithms with multiple steps can be made con gurable by allowing subclasses to
provide behavior for some of the steps.
What is it ?
The template method pattern factors out the common code among its
subclasses and puts them into the abstract class. The variable parts of the
algorithm are left for the subclasses to override. These parts are template
methods. A template method defines an algorithm in terms of abstract
operations that subclasses override to provide concrete behavior. The
ordering of the steps is fixed by the abstract class. Usually, the algorithm
is represented as a series of methods which are then invoked in the
desired sequence in another method. Note that the classes may choose to
ignore overriding certain steps or choose to rely on the default
implementation provided by the abstract class. The abstract class may
want to forbid the subclasses from overriding behavior for some steps, it
can enforce this constraint by marking the methods implementing those
steps as final .
One of the benefits apart from code reuse of the template method pattern
is that the higher level components don't depend on lower level
components and call the lower level components as and when required.
When entities at various levels depend horizontally and vertically on
various other entities, it becomes what is called a Dependency Rot. The
pattern helps avoid the dependency rot by making the lower level
components (subclasses) depend on the higher level abstract class.
Class Diagram
Abstract Class
Concrete Class
Example
Let's take our aircraft example. Before each flight there's a list of tasks the
pilots must go through that is called the "pre-flight checklist". You can well
imagine that for most of the aircrafts this list would have a lot of
commonalities. Therefore, it makes sense to model an abstract class that
captures all the tasks in the preflight checklist in the order they should be
performed.
@Override
void checkAirPressure() {
// Implement the custom logic for checking cockpit
// air pressure for F-16
}
@Override
protected boolean doorsLocked() {
// F-16 unlike a Boeing-747 has no doors
// so always return true;
return true;
}
}
Other Examples
Java's applets have gone down in popularity but the applet
framework exposed a number of hooks for the developers. For
instance, the start method gave the application a chance to take
action before the applet just got displayed in the browser.
Caveats
Don't confuse the template method pattern with the strategy pattern.
Strategy pattern uses composition by accepting objects that define
the entire algorithm, whereas the template pattern method uses
inheritance to vary parts of the algorithm by subclasses but the
outline and structure of the algorithm is still the realm of the abstract
class.
This lesson discusses how a set of policies, algorithms or strategies can be made interchangeable without
affecting the clients using them.
What is it ?
Class Diagram
Strategy
Concrete Strategy
Context
Examples
Concrete algorithms implement the same interface. The context has the
data the algorithm will act on. Together the context and the strategy
interact to implement the chosen algorithm. Usually, clients instantiate
the context and pass in the strategy object and then, only interact with the
context object.
The most trivial example one can think of is the family of sorting
algorithms. Say our application is only concerned with sorting integer
arrays. All the sorting algorithms can implement a common interface,
that we call ISort
@Override
public void sort(int[] input) {
// Do inefficient sorting in order n squared
}
}
public class MergeSort implements ISort {
@Override
public void sort(int[] input) {
// Do efficient sorting in nlogn
}
}
The Context class holds a reference to the strategy object and when it
receives requests from its clients, it forwards them to the strategy object
along with the required data.
void crunchingNumbers() {
// Do remaining work
}
}
Other Examples
For our aircraft scenario, we can think about the different ways an F-
16 can be equipped with weapons before each mission. An F-16 can
go for reconnaissance without carrying any weapons, it can be
loaded with (God forbid) nuclear weapons or it can carry Sidewinder
missiles to intercept incoming enemy fighter jets. When modeling
this scenario in our code, we could create a ArmingStrategy interface
which will have concrete implementations of NoWeapons ,
NuclearWeapons and AirToAirWeapons as arming strategies for the
plane. Before the F16 flies each mission we can set the
armingStrategy variable held in the F16 class with the desired
arming strategy for the mission.
Think how a text editor such as Microsoft Word can make use of the
strategy pattern when a client chooses the paragraph alignment
options. The strategies could be justify text, left-align, right-align
or center-align.
Caveats
The context can either pass the required data or itself to the strategy
object. In the latter case, the context would expose methods on itself
so that the strategy object can retrieve the required data.
This lesson discusses the visitor pattern which adds new behavior to composites without modifying the
composite's or it's underlying elements' classes.
What is it ?
Recall the Airforce class example from the Composite Pattern lesson. The
Airforce class is a composite consisting of several different kinds of
airplanes. It can be thought of as the object structure on whose elements
we want to conduct operations. The elements would be the individual
planes that make up the airforce object structure.
Say if we are tasked with monitoring of various metrics for each aircraft
such as remaining fuel, altitude and temperature then one option would
be to build this functionality inside the abstract class of all the airplanes.
The consequence would be that we'll need to implement the new methods
in all the airplane subclasses. Now imagine, a few days later we are
tasked with calculating the total price tag for the Airforce. We will now
add another method to the abstract airplane class or interface that'll
return the price for each individual plane and sum it across all the
airplanes.
There are several problems in our scenario, first the airplane class
shouldn't be responsible for monitoring or pricing data. It should just
represent the aircraft. With each additional functionality, we'll end up
bloating our aircraft classes with new unrelated methods. The visitor
patterns lets us out of this dilemma by suggesting to have a separate class
that defines the new functionality related to the aircraft. The methods in
the AircraftVisitor class would take the aircraft object as an argument
and work on it. This saves us from changing our aircraft classes each time
we need to support a new functionality relating to the Airforce class.
Class Diagram
Visitor
Concrete Visitor
Element
Concrete Element
Object Structure
Class Diagram
Example
Let's revisit our airforce example. The airforce class is the object structure
on which we desire to introduce two new operations, one for collecting
metrics for all the planes and two the cost of each aircraft. Let's see the
Airforce
@Override
public void accept(IAircraftVisitor visitor) {
visitor.visitF16(this);
}
}
@Override
public void accept(IAircraftVisitor visitor) {
visitor.visitBoeing747(this);
}
}
Now we'll define the interface IVisitor and the two concrete visitors .
Notice how the visitor interface defines a visit method for each of the
concrete types that make up the object structure. Say if a new airplane C-
130 was added to the object structure then the IAircraftVisitor would
need to introduce a new method visitC130 . The visitor interface allows
each aircraft to pass itself to the visitor by calling the correponding visit
method for its class on the visitor object. The visitor classes are given
below:
}
}
@Override
public void visitF16(F16 f16) {
// Logic to get price for F16
}
@Override
public void visitBoeing747(Boeing747 boeing747) {
// Logic to get price for Boeing 747
}
}
}
Note that each visitor can invoke methods specific to each concrete class.
Even though the two airplane classes share the same interface, the
pattern allows us to work with classes that are unrelated or don't share a
common interface. Finally, the client code will look like below:
while (planes.hasNext()){
planes.next().accept(aircraftVisitor);
}
aircraftVisitor.printAccumulatedResults();
}
}
Each object structure will have an associated visitor class. This visitor
interface will need to declare a visitConcreteElement operation for each
class of concreteElement defining the object structure. Each visit method
on the visitor interface will need to declare its argument to be a particular
concreteElement, allowing the visitor to access the interface of the
concreteElement directly.
Double Dispatch
Take a look at the following code snippet and run it. Even though we save
the reference for the BetterF16 object in a variable of the super class F16 ,
the outputs are printed for each of the object types correctly. This is an
example of dynamic dispatch where Java determines at runtime what
class an instance belongs to and chooses the appropriate, possibly
overridden, method.
class Demonstration {
public static void main( String args[] ) {
F16 f16 = new F16();
f16.whoAmI();
System.out.println();
// Reference for the derived object
// is held in the superclass type
F16 betterF16 = new BetterF16();
betterF16.whoAmI();
}
}
class F16 {
Now consider the below code snippet. We add a method fire() which
takes in an object of type Missile . We overload the fire method with an
object of a derived class BetterMissile .
class Demonstration {
public static void main( String args[] ) {
F16 f16 = new F16();
F16 betterF16 = new BetterF16();
Missile missile = new Missile();
Missile betterMissile = new BetterMissile();
System.out.println("Expected output");
f16.fireMissile(missile);
betterF16.fireMissile(missile);
System.out.println();
System.out.println("Expected output");
BetterMissile reallyBetterMissile = new BetterMissile();
f16.fireMissile(reallyBetterMissile);
betterF16.fireMissile(reallyBetterMissile);
System.out.println();
}
}
class BetterMissile extends Missile {
@Override
public String explode() {
return " very very big baaam";
}
}
class Missile {
class F16 {
If you run the above code the lines 14-15 call the fire with a reference of
type Missile pointing to an object of type BetterMissile . The JVM doesn't
check the type of the object at runtime and invokes fire( Missile ) instead
of fire( BetterMissile ) . Languages which support double dispatch or
multiple dispatch, would have invoked the right intended method
fire( BetterMissile ) .
In the visitor pattern the accept() method simulates the double dispatch
effect.
The first dispatch is when an airplane object calls the accept method. If
the airplane object is of type F16 or Boeing747 , the corresponding accept
method on those classes is called. The second dispatch happens when the
visitor interface reference passed into the accept method correctly calls
the corresponding visit method on the concrete visitor object the
reference points to.
Other Examples
Caveats
The visitor pattern cautions that if one expects the object structure
classes to change often then it might be a good idea to just keep the
new functionality within the visited classes instead of using the
visitor pattern. The key consideration in applying the Visitor pattern
is if the algorithm applied over an object structure is more likely to
change or the classes of objects that make up the structure. The
visitor class hierarchy can be difficult to maintain when new
concrete element classes are added frequently. In such cases, it's
Adding new concrete classes will require modifying all the visitor
classes, which makes it hard to add new types to the object structure.
Iteration over the object structure can happen via an iterator, inside
the object structure or by the visitor.
Summary
Pattern Purpose
clone() method of
java.lang.Object is an example
of this pattern.
java.util.AbstractSet and
java.util.AbstractMap are
examples of this pattern.
Closing remarks
The frequency with which you see patterns applied will depend on what
company or product you work for. If you are working in an off-shore
small scale wordpress-website-building sweat shop, chances are slim
you'll work on any of the patterns. The cost benefits are just not there. If
you are working on a website of ten pages and your customer will pay
you $2000 then neither you nor your boss would be inclined to spend the
effort and energy in going the extra mile and using nifty patterns in code.
However, for a project of $200,000 you better use design patterns where
applicable or your customer will not be happy when change
requirements start to cost him a fortune because you need to cut and rip
apart the code and start all over again.
The foremost benefit you should have from this course is to learn the
design pattern vocabulary. Being cognizant of what a builder pattern or a
Last but not the least, I would like to caution to not get overboard with
design patterns and apply them for the sake of applying design patterns.
They are a means to an end, not the end in themselves. The end being
maintainable, extensible and flexible code. Keep it simple and apply
patterns when and where it makes sense.
C. H. Afzal.
21st Sept, 2018
taught with passion and crafted with ❤in Santa Clara, U.S.A.
Credits
Every great product is a result of team-effort and so is this course. Our
team members include: