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 responsibilityis 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 responsibilityis 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
Requestwithsizefield, and handlers extendingHandler(withHandlerfieldsuccessor):HandlerFirstImplprocess 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 < 0where handled, so the last case issize = 0) otherwise pushes them to the end-pointsuccessor:HandlerThirdImpl. - Java SE example:
java.util.logging.Logger#log()
- theory:
commandis used to encapsulate all information needed to perform an action. Four terms always associated with the command pattern arecommand,receiver,invokerandclient:
commandobject knows aboutreceiverand invokes a method of thereceiver.
receiveris stored in thecommandobject by aggregation and does the work when theexecute()method incommandis called.
invokerobject knows how to execute acommand, but does not know anything about a concretecommand, it knows only aboutcommandinterface.
clientholdsinvoker,commandandreceiverobjects. - 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:MacFileSystemReceiverandWindowsFileSystemReceiver. For invoking commands:FileInvoker. - Java SE example:
all implementations of java.lang.Runnable
- theory:
interpreteris used to define easily-extendable grammar for instructions that form part of a language or notation. - code: We have an
expressioncontaining words and spaces, and we want to develop a grammar that enables us to answer such questions: check ifexpressioncontains specific wordLiteralExpressionand furthermore we could connect questions with basic binary operators:or(OrExpression) &and(AndExpression) to compose more complex questions like: check ifexpressioncontainsword1 and (word2 or (word3 or word4))(SpecificInterpreter). - Java SE example:
java.util.Pattern
- theory:
mediatoris 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
ChatMediatorthat facilitates sending messages betweenUsers. - Java SE example:
java.util.concurrent.ExecutorService (submit() methods)
- theory:
mementois 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
TextEditorwith 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
Earthwith observers:AsteroidObserverandSpaceStationObserverthat observeFlyingObject(with appropriateFlyingObjectType)flyingnearEarth.
- theory:
strategyis used to create an interchangeable family of algorithms from which the required process is chosen at run-time. Most known example ofstrategyisCollections.sort(List<T> list, Comparator<? super T> c). - code: We have a
ShoppingCartand we pay usingCreditCardPaymentorPayPalPayment(different algorithms). In theShoppingCartwe 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 methodis used to define an algorithm in a base class using abstract operations that subclasses provide with concrete behavior. -
code: We have a base class
Tripwith template methodperformTrip()that consists of:destinationTransport(start)daysSchedule(vacations)homeTransport(end)
We provide
TwoDayTrip extends Tripand 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:
visitoris 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 ofRouterby 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 factoryis an interface for creating families of related or dependent objects without specifying their concrete classes. - code: We have two possible platforms -
MacandMS, 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 ofwindowsandbuttonsintoPlatformDependentWidgetProducerand in run-time we decide which factory (MsWindowsWidgetFactoryorMacOSXWidgetFactory) should be used (it depend on which platform we are).
- theory:
builderis 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):immutableandmutable. - code: For
immutablewe 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.
Formutableobjects we haveGenericBuilder(usingjava 8) that could be used for everymutableclass. - Java SE example:
java.lang.StringBuilder#append()
- theory:
factory methoddefines an interface for creating an object, but leaves the choice of its type to the subclasses, creation being deferred at run-time. - code:
ShapeFactoryproduces differentShapesdepending onShapeType. - Java SE example:
java.util.ResourceBundle#getBundle()
- theory:
prototypeis 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
TimeConsumingCreationBaseFontthat construction takes two seconds (TimeUnit.SECONDS.sleep(2)in constructor), but this has alsoclone()method, so we have toprototypeobject 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:
singletonis 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 everysingletonis 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:
adapteris 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 -adapteris 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 adapterFrenchPersonAdapterthat implementsIPerson. So we have easy way to transform every Frenchman to Englishman. - Java SE example:
java.util.Arrays#asList()
- theory:
bridgeis 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:
SpotifyandTidalthat differs in technical implementations, butAbstractMusicPlayerthat is used as abridge, provides that we could easy switch between them.
- theory:
compositeis 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
ComplexShapethat is a composition ofShapes, and everyShapecould be decomposed toBasicShapes. - Java SE example:
java.util.Pattern
- theory:
decoratoris 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:LuxuryCarandSportsCar. 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 useCarDecoratorand 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:
facadeis used to define a simplified interface to a more complex subsystem. It is also very useful whenAPIis 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:
flyweightis used to reduce the memory (and time consuming creation) usage for complex models using numerous, but the same objects. - code: We have immutable
Fontthat 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. InFontclass we have static classFlyweightFactorythat is responsible for creating and cachingFontobject. Note that good practice is thatFontis immutable and has private constructor. - Java SE example:
java.lang.Integer#valueOf(int)
- theory:
proxyis a placeholder class that has plenty applications. There are many different kind ofproxy, but we decide to show onlyCache Proxy. Acache proxyimproves 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 ofproxyis 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%