The main goal of this project is to show basic design
patterns and provide real-life examples.
Solutions provided in the code are inspired by the wide-ranging internet
search and know-how of the author.
Valuable references:
http://www.blackwasp.co.uk/GofPatterns.aspx
http://www.oodesign.com/
Most of design patterns are given with the example from Java SE.
All behavioural design patterns are in package: behavioural.*.
Package of specific pattern is obtained using camel-case.
Every design pattern has been tested in test
package (same naming
convention).
- theory:
chain of responsibility
is used to process varied requests, each of which may be dealt with by a different handler. The most usual example of a machine using thechain of responsibility
is the vending machine coin slot: rather than having a slot for each type of coin, the machine has only one slot for all of them. - code: We have
Request
withsize
field, and handlers extendingHandler
(withHandler
fieldsuccessor
):HandlerFirstImpl
process requests of positive size otherwise pushes them to thesuccessor
:HandlerSecondImpl
, which process requests of negative size (note that at that point, all requests with eithersize > 0 & size < 0
where handled, so the last case issize = 0
) otherwise pushes them to the end-pointsuccessor
:HandlerThirdImpl
. - Java SE example:
java.util.logging.Logger#log()
- theory:
command
is used to encapsulate all information needed to perform an action. Four terms always associated with the command pattern arecommand
,receiver
,invoker
andclient
:
command
object knows aboutreceiver
and invokes a method of thereceiver
.
receiver
is stored in thecommand
object by aggregation and does the work when theexecute()
method incommand
is called.
invoker
object knows how to execute acommand
, but does not know anything about a concretecommand
, it knows only aboutcommand
interface.
client
holdsinvoker
,command
andreceiver
objects. - code: We have different platforms (Mac and Windows) and we want to
perform action on files (open, write, close). We have platform
dependent
FileSystemReceiver
(interface with methods open, write, close) implementations:MacFileSystemReceiver
andWindowsFileSystemReceiver
. For invoking commands:FileInvoker
. - Java SE example:
all implementations of java.lang.Runnable
- theory:
interpreter
is used to define easily-extendable grammar for instructions that form part of a language or notation. - code: We have an
expression
containing words and spaces, and we want to develop a grammar that enables us to answer such questions: check ifexpression
contains specific wordLiteralExpression
and furthermore we could connect questions with basic binary operators:or
(OrExpression
) &and
(AndExpression
) to compose more complex questions like: check ifexpression
containsword1 and (word2 or (word3 or word4))
(SpecificInterpreter
). - Java SE example:
java.util.Pattern
- theory:
mediator
is used to reduce coupling between classes that communicate with each other by developing mechanism to facilitate the interaction between objects in a manner in which objects are not aware of the existence of other objects. - code: We have a
ChatMediator
that facilitates sending messages betweenUsers
. - Java SE example:
java.util.concurrent.ExecutorService (submit() methods)
- theory:
memento
is used to save the current state of an object in such a manner that it can be restored at a later time without breaking the rules of encapsulation. - code: We have a
TextEditor
with functionality ofaddWord(String word)
. If we make a mistake we callundo()
.
- theory: In the observer pattern, an object, called the subject, maintains a list of other objects, which are its observers. When the state of the subject changes, its observers are notified.
- code: We have subject
Earth
with observers:AsteroidObserver
andSpaceStationObserver
that observeFlyingObject
(with appropriateFlyingObjectType
)flying
nearEarth
.
- theory:
strategy
is used to create an interchangeable family of algorithms from which the required process is chosen at run-time. Most known example ofstrategy
isCollections.sort(List<T> list, Comparator<? super T> c)
. - code: We have a
ShoppingCart
and we pay usingCreditCardPayment
orPayPalPayment
(different algorithms). In theShoppingCart
we have a methodpay(Payment method)
where we put appropriate payment algorithm. - Java SE example:
java.util.Collections# sort(List<T> list, Comparator<? super T> c)
-
theory:
template method
is used to define an algorithm in a base class using abstract operations that subclasses provide with concrete behavior. -
code: We have a base class
Trip
with template methodperformTrip()
that consists of:destinationTransport
(start)daysSchedule
(vacations)homeTransport
(end)
We provide
TwoDayTrip extends Trip
and we can compose two day trip like:TwoDayTrip twoDayTrip = new TwoDayTrip( () -> TransportType.TRAIN, () -> PlaceType.SEA, () -> PlaceType.SEA, () -> TransportType.PLANE );
-
Java SE example:
java.io.InputStream #read(byte b[], int off, int len)
- theory:
visitor
is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existent object structures without modifying the structures. - code: We have different configurations (
LinuxConfigurator
,MacConfigurator
) of different routers (DLinkRouter
,LinkSysRouter
) and we could easily add another type configuration (ex.MSConfigurator
) without changing code ofRouters
, and we could add another type ofRouter
by simply modifying code ofConfigurator
(RouterVisitor
). - Java SE example:
java.nio.file.SimpleFileVisitor
All creational design patterns are in package: creational.*.
Package of specific pattern is obtained using camel-case.
Every design pattern has been tested in test
package (same naming
convention).
- theory:
abstract factory
is an interface for creating families of related or dependent objects without specifying their concrete classes. - code: We have two possible platforms -
Mac
andMS
, and we have to produce desktop application that runs on either of them. As we know widgets implementation differ on different platforms, so we have to encapsulate production ofwindows
andbuttons
intoPlatformDependentWidgetProducer
and in run-time we decide which factory (MsWindowsWidgetFactory
orMacOSXWidgetFactory
) should be used (it depend on which platform we are).
- theory:
builder
is an external class that facilitates the construction of an object. Note that we could have two different types of objects (by their ability to change state):immutable
andmutable
. - code: For
immutable
we have:ImmutableWithBuilder
- we have private constructor that has a builder argument constructor and we have static classBuilder
(with same fields that an original class) that have setters that allow chaining.
Formutable
objects we haveGenericBuilder
(usingjava 8
) that could be used for everymutable
class. - Java SE example:
java.lang.StringBuilder#append()
- theory:
factory method
defines an interface for creating an object, but leaves the choice of its type to the subclasses, creation being deferred at run-time. - code:
ShapeFactory
produces differentShapes
depending onShapeType
. - Java SE example:
java.util.ResourceBundle#getBundle()
- theory:
prototype
is used to instantiate a new object by copying all of the fields of an existing object (construction of a new object could be time consuming). - code: We have
TimeConsumingCreationBaseFont
that construction takes two seconds (TimeUnit.SECONDS.sleep(2)
in constructor), but this has alsoclone()
method, so we have toprototype
object only once and then copy whenever we need new object (HelveticaFont
). - Java SE example:
java.lang.Object#clone() (the class has to implement java.lang.Cloneable)
- theory:
singleton
is used when we have to ensure that only one object of a particular class is ever created, and all references refer to the same object. - code:
ThreadSafeSingleton
. Note that not everysingleton
is thread safe, which is common mistake. - Java SE example:
java.lang.System#console()
All structural design patterns are in package: creational.*.
Package of specific pattern is obtained using camel-case.
Every design pattern has been tested in test
package (same naming
convention).
- theory:
adapter
is used when we have two incompatible types and we want to have a communication between them. When one class relies on the interface that the other does not implement -adapter
is a translator between them. - example: We have two incompatible interfaces of the same purpose: one in
english (
IPerson
), one in french (IFrenchPerson
) - we integrate these two system into one - using adapterFrenchPersonAdapter
that implementsIPerson
. So we have easy way to transform every Frenchman to Englishman. - Java SE example:
java.util.Arrays#asList()
- theory:
bridge
is used when we want to separate abstractions from technical implementation - implementation details could be changed easily then. - example: We have two different implementations of music player:
Spotify
andTidal
that differs in technical implementations, butAbstractMusicPlayer
that is used as abridge
, provides that we could easy switch between them.
- theory:
composite
is used when creating hierarchical object models. It is used to provide that the individual objects and groups can be accessed in the same manner. - example: We have
ComplexShape
that is a composition ofShapes
, and everyShape
could be decomposed toBasicShapes
. - Java SE example:
java.util.Pattern
- theory:
decorator
is used when we want to extend implementation in run-time (as an alternative to inheritance). It is obtain by wrapping classes in an object calleddecorator
. - example: We have
BasicCar implements Car
, and we want to have two upgrades:LuxuryCar
andSportsCar
. Using inheritance will cause a lot of problems - for example wiring dependencies between them (what in the case of another types, for exampleConvertibleCar
?!), so we decide to useCarDecorator
and two appropriate classes that extends it:LuxuryCar
,SportsCar
. Dependency is decoupled then - we could easily make Luxury-Sport car and Sport-Luxury car (and they are, like in real life, not the same thing!). - Java SE example:
I/O Streams
- theory:
facade
is used to define a simplified interface to a more complex subsystem. It is also very useful whenAPI
is still under construction and change rapidly, and we don't want to make perpetual changes in our code. - code: We want to book hotel (
HotelBooking
) and flight (FlightBooking
) in one place (TravelBooking
). More sophisticated and real life example is in my another project: https://github.com/mtumilowicz/reports#pdf-1PdfCellBuilder
- easy builder for very complex task of building a pdf call.
- theory:
flyweight
is used to reduce the memory (and time consuming creation) usage for complex models using numerous, but the same objects. - code: We have immutable
Font
that has three factors:name
,size
,color
. Creation of font could be time consuming task, so it's better to cache already created fonts and reuse them. Simple reports could demand thousands of different fonts. InFont
class we have static classFlyweightFactory
that is responsible for creating and cachingFont
object. Note that good practice is thatFont
is immutable and has private constructor. - Java SE example:
java.lang.Integer#valueOf(int)
- theory:
proxy
is a placeholder class that has plenty applications. There are many different kind ofproxy
, but we decide to show onlyCache Proxy
. Acache proxy
improves the performance of long-running tasks by caching request-answer. Note that answer should be always the same for specific input. Good example are problems with prime numbers.
Other type ofproxy
is described here: https://developer.jboss.org/blogs/stuartdouglas/2010/10/12/weld-cdi-and-proxies - code: We have algorithm from my another project
(https://github.com/mtumilowicz/poligon#CountPrimes) that is already
optimal and lightning-fast, but we could shorten the time of receiving
count using
proxy
(only first request is evaluated, others are taken from cache).
Coverage: 91%