Design Pattern
Design Pattern
2 Adapter Pattern 13
Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3 An introduction to Flowcharts 18
Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
8 Command Pattern 44
Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
1
Contents
24 Design data structures and algorithms for in-memory file system 137
Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
2
Contents
3
Contents
4
Chapter 1
UML class diagram example for the Abstract Factory Design Pattern.
5
Chapter 1. Abstract Factory Pattern
objects.
• ConcreteFactory : Implements the operations declared in the AbstractFactory to
create concrete product objects.
• Product : Defines a product object to be created by the corresponding concrete
factory and implements the AbstractProduct interface.
• Client : Uses only interfaces declared by AbstractFactory and AbstractProduct
classes.
Abstract Factory provides interfaces for creating families of related or dependent objects
without specifying their concrete classes.
Client software creates a concrete implementation of the abstract factory and then uses the
generic interfaces to create the concrete objects that are part of the family of objects.
The client does not know or care which concrete objects it gets from each of these concrete
factories since it uses only the generic interfaces of their products.
So with this idea of Abstract Factory pattern, we will now try to create a design that will
facilitate the creation of related objects.
Implementation
Let’s take an example, Suppose we want to build a global car factory. If it was factory
design pattern, then it was suitable for a single location. But for this pattern, we need
multiple locations and some critical design changes.
We need car factories in each location like IndiaCarFactory, USACarFactory and Default-
CarFactory. Now, our application should be smart enough to identify the location where
it is being used, so we should be able to use appropriate car factory without even knowing
which car factory implementation will be used internally. This also saves us from someone
calling wrong factory for a particular location.
Here we need another layer of abstraction which will identify the location and internally use
correct car factory implementation without even giving a single hint to user. This is exactly
the problem, which abstract factory pattern is used to solve.
6
Chapter 1. Abstract Factory Pattern
7
Chapter 1. Abstract Factory Pattern
MicroCar(Location location)
{
super(CarType.MICRO, location);
construct();
}
@Override
protected void construct()
{
System.out.println("Connecting to Micro Car ");
}
}
class MiniCar extends Car
{
MiniCar(Location location)
{
super(CarType.MINI,location );
construct();
}
@Override
void construct()
{
System.out.println("Connecting to Mini car");
}
}
enum Location
{
DEFAULT, USA, INDIA
}
class INDIACarFactory
{
static Car buildCar(CarType model)
{
Car car = null;
switch (model)
{
case MICRO:
car = new MicroCar(Location.INDIA);
break;
case MINI:
car = new MiniCar(Location.INDIA);
break;
case LUXURY:
8
Chapter 1. Abstract Factory Pattern
9
Chapter 1. Abstract Factory Pattern
break;
case MINI:
car = new MiniCar(Location.USA);
break;
case LUXURY:
car = new LuxuryCar(Location.USA);
break;
default:
break;
}
return car;
}
}
class CarFactory
{
private CarFactory()
{
}
public static Car buildCar(CarType type)
{
Car car = null;
// We can add any GPS Function here which
// read location property somewhere from configuration
// and use location specific car factory
// Currently I'm just using INDIA as Location
Location location = Location.INDIA;
switch(location)
{
case USA:
car = USACarFactory.buildCar(type);
break;
case INDIA:
car = INDIACarFactory.buildCar(type);
break;
default:
car = DefaultCarFactory.buildCar(type);
10
Chapter 1. Abstract Factory Pattern
}
return car;
}
}
class AbstractDesign
{
public static void main(String[] args)
{
System.out.println(CarFactory.buildCar(CarType.MICRO));
System.out.println(CarFactory.buildCar(CarType.MINI));
System.out.println(CarFactory.buildCar(CarType.LUXURY));
}
}
Output :
Difference
• The main difference between a “factory method” and an “abstract factory” is that the
factory method is a single method, and an abstract factory is an object.
• The factory method is just a method, it can be overridden in a subclass, whereas the
abstract factory is an object that has multiple factory methods on it.
• The Factory Method pattern uses inheritance and relies on a subclass to handle the
desired object instantiation.
Advantages
This pattern is particularly useful when the client doesn’t know exactly what type to create.
• Isolation of concrete classes: The Abstract Factory pattern helps you control
the classes of objects that an application creates. Because a factory encapsulates
the responsibility and the process of creating product objects, it isolates clients from
implementation classes. Clients manipulate instances through their abstract interfaces.
Product class names are isolated in the implementation of the concrete factory; they
do not appear in client code.
11
Chapter 1. Abstract Factory Pattern
• Exchanging Product Families easily: The class of a concrete factory appears only
once in an application, that is where it’s instantiated. This makes it easy to change the
concrete factory an application uses. It can use various product configurations simply
by changing the concrete factory. Because an abstract factory creates a complete
family of products, the whole product family changes at once.
• Promoting consistency among products: When product objects in a family are
designed to work together, it’s important that an application use objects from only
one family at a time. AbstractFactory makes this easy to enforce.n.
Disadvantages
NOTE :
Somewhat the above example is also based on How the Cabs like uber and ola functions on
the large scale.
Source
https://www.geeksforgeeks.org/abstract-factory-pattern/
12
Chapter 2
Adapter Pattern
1. The client makes a request to the adapter by calling a method on it using the target
interface.
2. The adapter translates that request on the adaptee using the adaptee interface.
3. Client receive the results of the call and is unaware of adapter’s presence.
Definition:
The adapter pattern convert the interface of a class into another interface clients expect.
Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
Class Diagram:
13
Chapter 2. Adapter Pattern
The client sees only the target interface and not the adapter. The adapter implements the
target interface. Adapter delegates all requests to Adaptee.
Example:
Suppose you have a Bird class with fly() , and makeSound()methods. And also a ToyDuck
class with squeak() method. Let’s assume that you are short on ToyDuck objects and
you would like to use Bird objects in their place. Birds have some similar functionality
but implement a different interface, so we can’t use them directly. So we will use adapter
pattern. Here our client would be ToyDuck and adaptee would be Bird.
Below is Java implementation of it.
14
Chapter 2. Adapter Pattern
15
Chapter 2. Adapter Pattern
Output:
Sparrow...
Flying
Chirp Chirp
ToyDuck...
Squeak
BirdAdapter...
Chirp Chirp
16
Chapter 2. Adapter Pattern
Here instead of having an adaptee object inside adapter (composition) to make use of its
functionality adapter inherits the adaptee.
Since multiple inheritance is not supported by many languages including java and is associ-
ated with many problems we have not shown implementation using class adapter pattern.
Advantages:
Disadvantages:
References:
Head First Design Patterns ( Book )
Source
https://www.geeksforgeeks.org/adapter-pattern/
17
Chapter 3
An introduction to Flowcharts
1. Terminal: The oval symbol indicates Start, Stop and Halt in a program’s logic flow. A
pause/halt is generally used in a program logic under some error conditions. Terminal
is the first and last symbols in the flowchart.
18
Chapter 3. An introduction to Flowcharts
5. Connectors: Whenever flowchart becomes complex or it spreads over more than one
page, it is useful to use connectors to avoid any confusions. It is represented by a
circle.
6. Flow lines: Flow lines indicate the exact sequence in which instructions are executed.
Arrows represent the direction of flow of control and relationship among different
symbols of flowchart.
Example : Draw a flowchart to input two numbers from user and display the
largest of two numbers
19
Chapter 3. An introduction to Flowcharts
C++
20
Chapter 3. An introduction to Flowcharts
else
largest = num2;
/*Print the largest number*/
cout << largest;
return 0;
}
Java
Output
30
References:
Computer Fundamentals by Pradeep K. Sinha and Priti Sinha
21
Chapter 3. An introduction to Flowcharts
Source
https://www.geeksforgeeks.org/an-introduction-to-flowcharts/
22
Chapter 4
1. Abstraction
2. Implementation
• The bridge pattern allows the Abstraction and the Implementation to be developed
independently and the client code can access only the Abstraction part without being
concerned about the Implementation part.
• The abstraction is an interface or abstract class and the implementor is also an interface
or abstract class.
• The abstraction contains a reference to the implementor. Children of the abstraction
are referred to as refined abstractions, and children of the implementor are concrete
implementors. Since we can change the reference to the implementor in the abstraction,
we are able to change the abstraction’s implementor at run-time. Changes to the
implementor do not affect client code.
• It increases the loose coupling between class abstraction and it’s implementation.
23
Chapter 4. Bridge Design Pattern
• Abstraction – core of the bridge design pattern and defines the crux. Contains a
reference to the implementer.
• Refined Abstraction – Extends the abstraction takes the finer detail one level below.
Hides the finer elements from implemetors.
• Implementer – It defines the interface for implementation classes. This interface
does not need to correspond directly to abstraction interface and can be very differ-
ent. Abstraction imp provides an implementation in terms of operations provided by
Implementer interface.
• Concrete Implementation – Implements the above implementer by providing con-
crete implementation.
24
Chapter 4. Bridge Design Pattern
protected Vehicle(Workshop workShop1, Workshop workShop2)
{
this.workShop1 = workShop1;
this.workShop2 = workShop2;
}
abstract public void manufacture();
}
// Refine abstraction 1 in bridge pattern
class Car extends Vehicle {
public Car(Workshop workShop1, Workshop workShop2)
{
super(workShop1, workShop2);
}
@Override public void manufacture()
{
System.out.print("Car ");
workShop1.work();
workShop2.work();
}
}
// Refine abstraction 2 in bridge pattern
class Bike extends Vehicle {
public Bike(Workshop workShop1, Workshop workShop2)
{
super(workShop1, workShop2);
}
@Override public void manufacture()
{
System.out.print("Bike ");
workShop1.work();
workShop2.work();
}
}
// Implementor for bridge pattern
interface Workshop
{
abstract public void work();
}
// Concrete implementation 1 for bridge pattern
25
Chapter 4. Bridge Design Pattern
Output :
Here we’re producing and assembling the two different vehicles using Bridge design pattern.
When we need bridge design pattern
The Bridge pattern is an application of the old advice, “prefer composition over inheritance”.
It becomes handy when you must subclass different times in ways that are orthogonal with
one another.
For Example, the above example can also be done something like this :
Without Bridge Design Pattern
26
Chapter 4. Bridge Design Pattern
But the above solution has a problem. If you want to change Bus class, then you may end
up changing ProduceBus and AssembleBus as well and if the change is workshop specific
then you may need to change Bike class as well.
With Bridge Design Pattern
You can solve the above problem by decoupling the Vehicle and Workshop interfaces in the
below manner.
Advantages
1. Bridge pattern decouple an abstraction from its implementation so that the two can
vary independently.
2. It is used mainly for implementing platform independence feature.
3. It adds one more method level redirection to achieve the objective.
27
Chapter 4. Bridge Design Pattern
4. Publish abstraction interface in a separate inheritance hierarchy, and put the imple-
mentation in its own inheritance hierarchy.
5. Use bridge pattern to run-time binding of the implementation.
6. Use bridge pattern to map orthogonal class hierarchies
7. Bridge is designed up-front to let the abstraction and the implementation vary inde-
pendently.
Improved By : sunny94
Source
https://www.geeksforgeeks.org/bridge-design-pattern/
28
Chapter 5
Source : Wikipedia
• Product – The product class defines the type of the complex object that is to be
generated by the builder pattern.
• Builder – This abstract base class defines all of the steps that must be taken in
order to correctly create a product. Each step is generally abstract as the actual
functionality of the builder is carried out in the concrete subclasses. The GetProduct
method is used to return the final product. The builder class is often replaced with a
simple interface.
29
Chapter 5. Builder Design Pattern
interface HousePlan
{
public void setBasement(String basement);
public void setStructure(String structure);
public void setRoof(String roof);
public void setInterior(String interior);
}
class House implements HousePlan
{
private String basement;
private String structure;
private String roof;
private String interior;
public void setBasement(String basement)
{
this.basement = basement;
}
public void setStructure(String structure)
{
this.structure = structure;
}
public void setRoof(String roof)
{
30
Chapter 5. Builder Design Pattern
this.roof = roof;
}
public void setInterior(String interior)
{
this.interior = interior;
}
}
interface HouseBuilder
{
public void buildBasement();
public void buildStructure();
public void bulidRoof();
public void buildInterior();
public House getHouse();
}
class IglooHouseBuilder implements HouseBuilder
{
private House house;
public IglooHouseBuilder()
{
this.house = new House();
}
public void buildBasement()
{
house.setBasement("Ice Bars");
}
public void buildStructure()
{
house.setStructure("Ice Blocks");
}
public void buildInterior()
{
house.setInterior("Ice Carvings");
}
31
Chapter 5. Builder Design Pattern
public void bulidRoof()
{
house.setRoof("Ice Dome");
}
public House getHouse()
{
return this.house;
}
}
class TipiHouseBuilder implements HouseBuilder
{
private House house;
public TipiHouseBuilder()
{
this.house = new House();
}
public void buildBasement()
{
house.setBasement("Wooden Poles");
}
public void buildStructure()
{
house.setStructure("Wood and Ice");
}
public void buildInterior()
{
house.setInterior("Fire Wood");
}
public void bulidRoof()
{
house.setRoof("Wood, caribou and seal skins");
}
public House getHouse()
{
return this.house;
}
}
32
Chapter 5. Builder Design Pattern
class CivilEngineer
{
private HouseBuilder houseBuilder;
public CivilEngineer(HouseBuilder houseBuilder)
{
this.houseBuilder = houseBuilder;
}
public House getHouse()
{
return this.houseBuilder.getHouse();
}
public void constructHouse()
{
this.houseBuilder.buildBasement();
this.houseBuilder.buildStructure();
this.houseBuilder.bulidRoof();
this.houseBuilder.buildInterior();
}
}
class Builder
{
public static void main(String[] args)
{
HouseBuilder iglooBuilder = new IglooHouseBuilder();
CivilEngineer engineer = new CivilEngineer(iglooBuilder);
engineer.constructHouse();
House house = engineer.getHouse();
System.out.println("Builder constructed: "+ house);
}
}
Output :
• The parameters to the constructor are reduced and are provided in highly readable
method calls.
33
Chapter 5. Builder Design Pattern
• Builder design pattern also helps in minimizing the number of parameters in construc-
tor and thus there is no need to pass in null for optional parameters to the constructor.
• Object is always instantiated in a complete state
• Immutable objects can be build without much complex logic in object building process.
• The number of lines of code increase at least to double in builder pattern, but the
effort pays off in terms of design flexibility and much more readable code.
• Requires creating a separate ConcreteBuilder for each different type of Product.
Source
https://www.geeksforgeeks.org/builder-design-pattern/
34
Chapter 6
Design components
• Business Delegate : A single entry point class for client entities to provide access
to Business Service methods.
• LookUp Service : Lookup service object is responsible to get relative business
implementation and provide business object access to business delegate object.
• Business Service : Business Service interface. Concrete classes implement this
business service to provide actual business implementation logic.
interface BusinessService
{
35
Chapter 6. Business Delegate Pattern
36
Chapter 6. Business Delegate Pattern
businessService.doProcessing();
}
}
class Client
{
BusinessDelegate businessService;
public Client(BusinessDelegate businessService)
{
this.businessService = businessService;
}
public void doTask()
{
businessService.doTask();
}
}
class BusinessDelegatePattern
{
public static void main(String[] args)
{
BusinessDelegate businessDelegate = new BusinessDelegate();
businessDelegate.setServiceType("One");
Client client = new Client(businessDelegate);
client.doTask();
businessDelegate.setServiceType("Two");
client.doTask();
}
}
Output:
Advantages :
• Business Delegate reduces coupling between presentation-tier clients and Business ser-
vices.
• The Business Delegate hides the underlying implementation details of the Business
service.
Disadvantages :
37
Chapter 6. Business Delegate Pattern
• Maintenance due the extra layer that increases the number of classes in the application.
Source
https://www.geeksforgeeks.org/business-delegate-pattern/
38
Chapter 7
This pattern is recommended when multiple objects can handle a request and the handler
doesn’t have to be a specific object. Also, handler is determined at runtime. Please note
that that a request not handled at all by any handler is a valid use case.
39
Chapter 7. Chain of Responsibility Design Pattern
• Handler : This can be an interface which will primarily recieve the request and
dispatches the request to chain of handlers. It has reference of only first handler in
the chain and does not know anything about rest of the handlers.
• Concrete handlers : These are actual handlers of the request chained in some
sequential order.
• Client : Originator of request and this will access the handler to handle it.
interface Chain
{
public abstract void setNext(Chain nextInChain);
public abstract void process(Number request);
40
Chapter 7. Chain of Responsibility Design Pattern
}
class Number
{
private int number;
public Number(int number)
{
this.number = number;
}
public int getNumber()
{
return number;
}
}
class NegativeProcessor implements Chain
{
private Chain nextInChain;
public void setNext(Chain c)
{
nextInChain = c;
}
public void process(Number request)
{
if (request.getNumber() < 0)
{
System.out.println("NegativeProcessor : " + request.getNumber());
}
else
{
nextInChain.process(request);
}
}
}
class ZeroProcessor implements Chain
{
private Chain nextInChain;
public void setNext(Chain c)
{
nextInChain = c;
41
Chapter 7. Chain of Responsibility Design Pattern
}
public void process(Number request)
{
if (request.getNumber() == 0)
{
System.out.println("ZeroProcessor : " + request.getNumber());
}
else
{
nextInChain.process(request);
}
}
}
class PositiveProcessor implements Chain
{
private Chain nextInChain;
public void setNext(Chain c)
{
nextInChain = c;
}
public void process(Number request)
{
if (request.getNumber() > 0)
{
System.out.println("PositiveProcessor : " + request.getNumber());
}
else
{
nextInChain.process(request);
}
}
}
class TestChain
{
public static void main(String[] args) {
//configure Chain of Responsibility
Chain c1 = new NegativeProcessor();
Chain c2 = new ZeroProcessor();
Chain c3 = new PositiveProcessor();
c1.setNext(c2);
c2.setNext(c3);
42
Chapter 7. Chain of Responsibility Design Pattern
Output :
PositiveProcessor : 90
NegativeProcessor : -50
ZeroProcessor : 0
PositiveProcessor : 91
• To reduce the coupling degree. Decoupling it will request the sender and receiver.
• Simplified object. The object does not need to know the chain structure.
• Enhance flexibility of object assigned duties. By changing the members within the
chain or change their order, allow dynamic adding or deleting responsibility.
• Increase the request processing new class of very convenient.
Source
https://www.geeksforgeeks.org/chain-responsibility-design-pattern/
43
Chapter 8
Command Pattern
if (buttonPressed == button1)
lights.on()
But we need to keep in mind that turning on some devices like stereo comprises of many
steps like setting cd, volume etc. Also we can reassign a button to do something else. By
using simple if-else we are coding to implementation rather than interface. Also there is
tight coupling.
So what we want to achieve is a design that provides loose coupling and remote control
should not have much information about a particular device. The command pattern helps
us do that.
Definition: The command pattern encapsulates a request as an object, thereby letting
us parameterize other objects with different requests, queue or log requests, and support
undoable operations.
The definition is a bit confusing at first but let’s step through it. In analogy to our problem
above remote control is the client and stereo, lights etc. are the receivers. In command
pattern there is a Command object that encapsulates a request by binding together a set of
actions on a specific receiver. It does so by exposing just one method execute() that causes
some actions to be invoked on the receiver.
Parameterizing other objects with different requests in our analogy means that the button
used to turn on the lights can later be used to turn on stereo or maybe open the garage
door.
44
Chapter 8. Command Pattern
queue or log requests, and support undoable operations means that Command’s Execute
operation can store state for reversing its effects in the Command itself. The Command may
have an added unExecute operation that reverses the effects of a previous call to execute.It
may also support logging changes so that they can be reapplied in case of a system crash.
Below is the Java implementation of above mentioned remote control example:
45
Chapter 8. Command Pattern
{
this.light = light;
}
public void execute()
{
light.off();
}
}
// Stereo and its command classes
class Stereo
{
public void on()
{
System.out.println("Stereo is on");
}
public void off()
{
System.out.println("Stereo is off");
}
public void setCD()
{
System.out.println("Stereo is set " +
"for CD input");
}
public void setDVD()
{
System.out.println("Stereo is set"+
" for DVD input");
}
public void setRadio()
{
System.out.println("Stereo is set" +
" for Radio");
}
public void setVolume(int volume)
{
// code to set the volume
System.out.println("Stereo volume set"
+ " to " + volume);
}
}
class StereoOffCommand implements Command
{
Stereo stereo;
public StereoOffCommand(Stereo stereo)
{
this.stereo = stereo;
46
Chapter 8. Command Pattern
}
public void execute()
{
stereo.off();
}
}
class StereoOnWithCDCommand implements Command
{
Stereo stereo;
public StereoOnWithCDCommand(Stereo stereo)
{
this.stereo = stereo;
}
public void execute()
{
stereo.on();
stereo.setCD();
stereo.setVolume(11);
}
}
// A Simple remote control with one button
class SimpleRemoteControl
{
Command slot; // only one button
public SimpleRemoteControl()
{
}
public void setCommand(Command command)
{
// set the command the remote will
// execute
slot = command;
}
public void buttonWasPressed()
{
slot.execute();
}
}
// Driver class
class RemoteControlTest
{
public static void main(String[] args)
{
47
Chapter 8. Command Pattern
SimpleRemoteControl remote =
new SimpleRemoteControl();
Light light = new Light();
Stereo stereo = new Stereo();
// we can change command dynamically
remote.setCommand(new
LightOnCommand(light));
remote.buttonWasPressed();
remote.setCommand(new
StereoOnWithCDCommand(stereo));
remote.buttonWasPressed();
remote.setCommand(new
StereoOffCommand(stereo));
remote.buttonWasPressed();
}
}
Output:
Light is on
Stereo is on
Stereo is set for CD input
Stereo volume set to 11
Stereo is off
Notice that the remote control doesn’t know anything about turning on the stereo. That
information is contained in a separate command object. This reduces the coupling between
them.
Advantages:
• Makes our code extensible as we can add new commands without changing existing
code.
• Reduces coupling the invoker and receiver of a command.
Disadvantages:
References:
Source
https://www.geeksforgeeks.org/command-pattern/
48
Chapter 9
1. Component – Component declares the interface for objects in the composition and
for accessing and managing its child components. It also implements default behavior
for the interface common to all classes as appropriate.
2. Leaf – Leaf defines behavior for primitive objects in the composition. It represents
leaf objects in the composition.
3. Composite – Composite stores child components and implements child related oper-
ations in the component interface.
4. Client – Client manipulates the objects in the composition through the component
interface.
Client use the component class interface to interact with objects in the composition structure.
If recipient is a leaf then request is handled directly. If recipient is a composite, then it
49
Chapter 9. Composite Design Pattern
usually forwards request to its child components, possibly performing additional operations
before and after forwarding.
Real Life example
In an organization, It have general managers and under general managers, there can be
managers and under managers there can be developers. Now you can set a tree structure
and ask each node to perform common operation like getSalary().
Composite design pattern treats each node in two ways:
1) Composite – Composite means it can have other objects below it.
2) leaf – leaf means it has no objects below it.
Tree structure:
The above figure shows a typical Composite object structure. As you can see, there can be
many children to a single parent i.e. Composite, but only one parent per child.
Interface Component.java
Leaf.java
50
Chapter 9. Composite Design Pattern
this.name = name;
this.position = position;
}
@Override
public void showEmployeeDetails()
{
System.out.println(empId+" " +name+);
}
}
Leaf.java
Composite.java
import java.util.ArrayList;
import java.util.List;
public class CompanyDirectory implements Employee
{
private List<Employee> employeeList = new ArrayList<Employee>();
@Override
public void showEmployeeDetails()
{
for(Employee emp:employeeList)
{
51
Chapter 9. Composite Design Pattern
emp.showEmployeeDetails();
}
}
public void addEmployee(Employee emp)
{
employeeList.add(emp);
}
public void removeEmployee(Employee emp)
{
employeeList.remove(emp);
}
}
Client.java
52
Chapter 9. Composite Design Pattern
53
Chapter 9. Composite Design Pattern
54
Chapter 9. Composite Design Pattern
{
employeeList.remove(emp);
}
}
// Driver class
public class Company
{
public static void main (String[] args)
{
Developer dev1 = new Developer(100, "Lokesh Sharma", "Pro Developer");
Developer dev2 = new Developer(101, "Vinay Sharma", "Developer");
CompanyDirectory engDirectory = new CompanyDirectory();
engDirectory.addEmployee(dev1);
engDirectory.addEmployee(dev2);
Manager man1 = new Manager(200, "Kushagra Garg", "SEO Manager");
Manager man2 = new Manager(201, "Vikram Sharma ", "Kushagra's Manager");
CompanyDirectory accDirectory = new CompanyDirectory();
accDirectory.addEmployee(man1);
accDirectory.addEmployee(man2);
CompanyDirectory directory = new CompanyDirectory();
directory.addEmployee(engDirectory);
directory.addEmployee(accDirectory);
directory.showEmployeeDetails();
}
}
Output :
1. Less number of objects reduces the memory usage, and it manages to keep us away
from errors related to memory like java.lang.OutOfMemoryError.
55
Chapter 9. Composite Design Pattern
2. Although creating an object in Java is really fast, we can still reduce the execution
time of our program by sharing objects.
Source
https://www.geeksforgeeks.org/composite-design-pattern/
56
Chapter 10
57
Chapter 10. Composite Design Pattern in C++
Here in this diagram, as you can see both composite and Leaf implements Component dia-
gram, thus allowing the same operation on both objects but the important part is Composite
Class which also contain the Component Objects which is symbolized by the black diamond
indicating composition relationship between Composite and Component class.
Then how to design our classes to accommodate such scenarios. We will try to understand it
by implementing our copy example. Say you have to create a page which has operations like
add, delete, remove and also a copy which will have the same operations as the individual
pages.
Such situation is the best dealt with the composite pattern.
58
Chapter 10. Composite Design Pattern in C++
class Page : public PageObject {
public:
void Add(PageObject a)
{
cout << "something is added to the page" << endl;
}
void Remove()
{
cout << "soemthing is removed from the page" << endl;
}
void Delete(PageObject a)
{
cout << "soemthing is deleted from page " << endl;
}
};
class Copy : public PageObject {
vector<PageObject> copyPages;
public:
void AddElement(PageObject a)
{
copyPages.push_back(a);
}
void Add(PageObject a)
{
cout << "something is added to the copy" << endl;
}
void Remove()
{
cout << "something is removed from the copy" << endl;
}
void Delete(PageObject a)
{
cout << "something is deleted from the copy";
}
};
int main()
{
Page a;
Page b;
Copy allcopy;
allcopy.AddElement(a);
allcopy.AddElement(b);
59
Chapter 10. Composite Design Pattern in C++
allcopy.Add(a);
a.Add(b);
allcopy.Remove();
b.Remove();
return 0;
}
Now the same operation that can be applied to an individual object and can also be applied
to the collection of those individual object makes it very easy to work with a larger object
which is made of the smaller independent objects.
The most notable example of the composite pattern is in any UI toolkit. Consider the case
of UI elements where each UI top-level UI element is composed of many smaller independent
lower-level UI elements and the both the top and lower level UI element respond to same
events and actions.
References :
1. Composite Pattern c#
2. Composite Pattern
Source
https://www.geeksforgeeks.org/composite-pattern-cpp/
60
Chapter 11
61
Chapter 11. Curiously recurring template pattern (CRTP)
protected:
int dimensionX;
int dimensionY;
};
// For Tiff Images
class TiffImage : public Image
{
public:
void Draw() { }
Dimension GetDimensionInPixels() {
return Dimension(dimensionX, dimensionY);
}
};
// There can be more derived classes like PngImage,
// BitmapImage, etc
// Driver code that calls virtual function
int main()
{
// An image type
Image* pImage = new TiffImage;
// Store time before virtual function calls
auto then = Clock::now();
// Call Draw 1000 times to make sure performance
// is visible
for (int i = 0; i < 1000; ++i)
pImage->Draw();
// Store time after virtual function calls
auto now = Clock::now();
cout << "Time taken: "
<< std::chrono::duration_cast
<std::chrono::nanoseconds>(now - then).count()
<< " nanoseconds" << endl;
return 0;
}
Output :
62
Chapter 11. Curiously recurring template pattern (CRTP)
When a method is declared virtual, compiler secretly does two things for us:
63
Chapter 11. Curiously recurring template pattern (CRTP)
// Driver code
int main()
{
// A base type pointer pointing to derived
CBase *pBase = new CDerived;
// Accessing vPtr
int* pVPtr = *(int**)pBase;
// Calling virtual method
((void(*)())pVPtr[0])();
// Changing vPtr
delete pBase;
pBase = new CBase;
pVPtr = *(int**)pBase;
// Calls method for new base object
((void(*)())pVPtr[0])();
return 0;
}
Output :
CDerived::Foo() called
CBase::Foo() called
We are able to access vPtr and able to make calls to virtual methods through it. The
memory representation of objects is explained here.
Is it wise to use virtual method?
As it can be seen, through base class pointer, call to derived class method is being dispatched.
Everything seems to be working fine. Then what is the problem?
If a virtual routine is called many times (order of hundreds of thousands), it drops the
performance of system, reason being each time the routine is called, its address needs to be
resolved by looking through VTable using VPtr. Extra indirection (pointer dereference) for
each call to a virtual method makes accessing VTable a costly operation and it is better to
avoid it as much as we can.
64
Chapter 11. Curiously recurring template pattern (CRTP)
65
Chapter 11. Curiously recurring template pattern (CRTP)
void Draw()
{
// Uncomment this to check method dispatch
// cout << "TiffImage::Draw() called" << endl;
}
Dimension GetDimensionInPixels()
{
return Dimension(dimensionX, dimensionY);
}
};
// There can be more derived classes like PngImage,
// BitmapImage, etc
// Driver code
int main()
{
// An Image type pointer pointing to Tiffimage
Image<TiffImage>* pImage = new TiffImage;
// Store time before virtual function calls
auto then = Clock::now();
// Call Draw 1000 times to make sure performance
// is visible
for (int i = 0; i < 1000; ++i)
pImage->Draw();
// Store time after virtual function calls
auto now = Clock::now();
cout << "Time taken: "
<< std::chrono::duration_cast
<std::chrono::nanoseconds>(now - then).count()
<< " nanoseconds" << endl;
return 0;
}
Output :
66
Chapter 11. Curiously recurring template pattern (CRTP)
note that the performance depends on a lot of factors like compiler used, operations per-
formed by virtual methods. Performance numbers might differ in different runs, but (small)
performance gain is expected from CRTP.
Note: If we print size of class in CRTP, it can bee seen that VPtr no longer reserves 4 bytes
of memory.
Source
https://www.geeksforgeeks.org/curiously-recurring-template-pattern-crtp-2/
67
Chapter 12
Design components
68
Chapter 12. Data Access Object Pattern
69
Chapter 12. Data Access Object Pattern
}
}
interface DeveloperDao
{
public List<Developer> getAllDevelopers();
public Developer getDeveloper(int DeveloperId);
public void updateDeveloper(Developer Developer);
public void deleteDeveloper(Developer Developer);
}
class DeveloperDaoImpl implements DeveloperDao
{
List<Developer> Developers;
public DeveloperDaoImpl()
{
Developers = new ArrayList<Developer>();
Developer Developer1 = new Developer("Kushagra",0);
Developer Developer2 = new Developer("Vikram",1);
Developers.add(Developer1);
Developers.add(Developer2);
}
@Override
public void deleteDeveloper(Developer Developer)
{
Developers.remove(Developer.getDeveloperId());
System.out.println("DeveloperId " + Developer.getDeveloperId()
+ ", deleted from database");
}
@Override
public List<Developer> getAllDevelopers()
{
return Developers;
}
@Override
public Developer getDeveloper(int DeveloperId)
{
return Developers.get(DeveloperId);
}
@Override
public void updateDeveloper(Developer Developer)
{
Developers.get(Developer.getDeveloperId()).setName(Developer.getName());
System.out.println("DeveloperId " + Developer.getDeveloperId()
+ ", updated in the database");
70
Chapter 12. Data Access Object Pattern
}
}
class DaoPatternDemo
{
public static void main(String[] args)
{
DeveloperDao DeveloperDao = new DeveloperDaoImpl();
for (Developer Developer : DeveloperDao.getAllDevelopers())
{
System.out.println("DeveloperId : " + Developer.getDeveloperId()
+ ", Name : " + Developer.getName());
}
Developer Developer =DeveloperDao.getAllDevelopers().get(0);
Developer.setName("Lokesh");
DeveloperDao.updateDeveloper(Developer);
DeveloperDao.getDeveloper(0);
System.out.println("DeveloperId : " + Developer.getDeveloperId()
+ ", Name : " + Developer.getName());
}
}
Output:
Advantages :
• The advantage of using data access objects is the relatively simple and rigorous sep-
aration between two important parts of an application that can but should not know
anything of each other, and which can be expected to evolve frequently and indepen-
dently.
• if we need to change the underlying persistence mechanism we only have to change
the DAO layer, and not all the places in the domain logic where the DAO layer is used
from.
Disadvantages :
71
Chapter 12. Data Access Object Pattern
Reference :
http://www.oracle.com/technetwork/java/dataaccessobject-138824.html
Source
https://www.geeksforgeeks.org/data-access-object-pattern/
72
Chapter 13
73
Chapter 13. Decorator Pattern | Set 1 (Background)
Each pizza has different cost. We have overridden the getCost() in the subclasses to find
the appropriate cost. Now suppose a new requirement, in addition to a pizza, customer can
also ask for several toppings such as Fresh Tomato, Paneer, Jalapeno, Capsicum, Barbeque,
etc. Let us think about for sometime that how do we accommodate changes in the above
classes so that customer can choose pizza with toppings and we get the total cost of pizza
and toppings the customer chooses.
Let us look at various options.
Option 1
Create a new subclass for every topping with a pizza. The class diagram would look
74
Chapter 13. Decorator Pattern | Set 1 (Background)
like:
This looks very complex. There are way too many classes and is a maintenance nightmare.
Also if we want to add a new topping or pizza we have to add so many classes. This is
obviously very bad design.
Option 2:
Let’s add instance variables to pizza base class to represent whether or not each pizza has a
75
Chapter 13. Decorator Pattern | Set 1 (Background)
76
Chapter 13. Decorator Pattern | Set 1 (Background)
This design looks good at first but lets take a look at the problems associated with it.
In short our design violates one of the most popular design principle – The Open-Closed
Principle which states that classes should be open for extension and closed for modification.
In the next set, we will be introducing Decorator Pattern and apply it to above above
problem.
References: Head First Design Patterns( Book).
Source
https://www.geeksforgeeks.org/decorator-pattern/
77
Chapter 14
78
Chapter 14. Decorator Pattern | Set 3 (Coding the Design)
79
Chapter 14. Decorator Pattern | Set 3 (Coding the Design)
80
Chapter 14. Decorator Pattern | Set 3 (Coding the Design)
}
class SimplePizza extends Pizza
{
public SimplePizza() { description = "SimplePizza"; }
public int getCost() { return 50; }
}
// Concrete toppings classes
class FreshTomato extends ToppingsDecorator
{
// we need a reference to obj we are decorating
Pizza pizza;
public FreshTomato(Pizza pizza) { this.pizza = pizza; }
public String getDescription() {
return pizza.getDescription() + ", Fresh Tomato ";
}
public int getCost() { return 40 + pizza.getCost(); }
}
class Barbeque extends ToppingsDecorator
{
Pizza pizza;
public Barbeque(Pizza pizza) { this.pizza = pizza; }
public String getDescription() {
return pizza.getDescription() + ", Barbeque ";
}
public int getCost() { return 90 + pizza.getCost(); }
}
class Paneer extends ToppingsDecorator
{
Pizza pizza;
public Paneer(Pizza pizza) { this.pizza = pizza; }
public String getDescription() {
return pizza.getDescription() + ", Paneer ";
}
public int getCost() { return 70 + pizza.getCost(); }
}
// Other toppings can be coded in a similar way
// Driver class and method
class PizzaStore
{
public static void main(String args[])
{
// create new margherita pizza
Pizza pizza = new Margherita();
System.out.println( pizza.getDescription() +
81
Chapter 14. Decorator Pattern | Set 3 (Coding the Design)
Output:
Notice how we can add/remove new pizzas and toppings with no alteration in previously
tested code and toppings and pizzas are decoupled.
82
Chapter 14. Decorator Pattern | Set 3 (Coding the Design)
return "MilkShake";
}
float price()
{
return 30;
}
};
// Decorator
class MilkShakeDecorator: public MilkShake
{
protected:
MilkShake *m_MilkShake;
public:
MilkShakeDecorator(MilkShake *baseMilkShake): m_MilkShake(baseMilkShake){}
string Serve()
{
return m_MilkShake->Serve();
}
float price()
{
return m_MilkShake->price();
}
};
// Concrete Decorator
class MangoMilkShake: public MilkShakeDecorator
{
public:
MangoMilkShake(MilkShake *baseMilkShake): MilkShakeDecorator(baseMilkShake){}
string Serve()
{
return m_MilkShake->Serve() + " decorated with Mango ";
}
float price()
{
return m_MilkShake->price() + 40;
}
};
class VanillaMilkShake: public MilkShakeDecorator
83
Chapter 14. Decorator Pattern | Set 3 (Coding the Design)
{
public:
VanillaMilkShake(MilkShake *baseMilkShake): MilkShakeDecorator(baseMilkShake){}
string Serve()
{
return m_MilkShake->Serve() + " decorated with Vanilla ";
}
float price()
{
return m_MilkShake->price() + 80;
}
};
int main()
{
MilkShake *baseMilkShake = new BaseMilkShake();
cout << "Basic Milk shake \n";
cout << baseMilkShake -> Serve() << endl;
cout << baseMilkShake -> price() << endl;
MilkShake *decoratedMilkShake = new MangoMilkShake(baseMilkShake);
cout << "Mango decorated Milk shake \n";
cout << decoratedMilkShake -> Serve() << endl;
cout << decoratedMilkShake -> price() << endl;
delete decoratedMilkShake;
decoratedMilkShake = new VanillaMilkShake(baseMilkShake);
cout << "Vanilla decorated Milk shake \n";
cout << decoratedMilkShake -> Serve() << endl;
cout << decoratedMilkShake -> price() << endl;
delete decoratedMilkShake;
delete baseMilkShake;
return 0;
}
Output:
84
Chapter 14. Decorator Pattern | Set 3 (Coding the Design)
Source
https://www.geeksforgeeks.org/decorator-pattern-set-3-coding-the-design/
85
Chapter 15
class Manager(object):
def __init__(self):
self.developers=[]
self.designers=[]
self.testers=[]
def addDeveloper(self,dev):
self.developers.append(dev)
86
Chapter 15. Dependency Inversion Principle (SOLID)
def addDesigners(self,design):
self.designers.append(design)
def addTesters(self,testers):
self.testers.append(testers)
class Developer(object):
def __init__(self):
print "developer added"
class Designer(object):
def __init__(self):
print "designer added"
class Testers(object):
def __init__(self):
print "tester added"
if __name__ == "__main__":
a=Manager()
a.addDeveloper(Developer())
a.addDesigners(Designer())
Output :
developer added
designer added
87
Chapter 15. Dependency Inversion Principle (SOLID)
Now, let’s look at the design loop holes in the source code :
First, you have exposed everything about the lower layer to the upper layer, thus abstraction
is not mentioned. That means Manager must already know about the type of the workers
that he can supervise.
Now if another type of worker comes under the manager lets say, QA (quality assurance),
then the whole class needs to be rejigged. This is where dependency inversion principle
pitch in.
Let’s see how to solve the problem to the better extent using Dependency Inversion Principle.
class Employee(object):
def Work():
pass
class Manager():
def __init__(self):
self.employees=[]
def addEmployee(self,a):
self.employees.append(a)
class Developer(Employee):
def __init__(self):
print "developer added"
88
Chapter 15. Dependency Inversion Principle (SOLID)
def Work():
print "turning coffee into code"
class Designer(Employee):
def __init__(self):
print "designer added"
def Work():
print "turning lines to wireframes"
class Testers(Employee):
def __init__(self):
print "tester added"
def Work():
print "testing everything out there"
if __name__ == "__main__":
a=Manager()
a.addEmployee(Developer())
a.addEmployee(Designer())
Output :
developer added
designer added
Now if any other kind of the employee is added it can be simply be added to Manager
without making the manager explicitly aware of it. Now to add another class of employee
we can simply call
class QA(Employee):
def Work():
print "testing everything out there"
a.add(QA())
The creation of the abstraction between different employees and Manager has resulted in
very good looking design code which is easily maintainable and extendable.Please have a
look into the UML diagram below.
89
Chapter 15. Dependency Inversion Principle (SOLID)
In this code, the manager doesn’t have an idea beforehand about all the type of workers
that may come under him/her making the code truly decoupled. There are many design
patterns where this is a core idea and other things are built upon it.
Source
https://www.geeksforgeeks.org/dependecy-inversion-principle-solid/
90
Chapter 16
Goal:
• Understand the problem and matching it with some pattern.
• Reusage of old interface or making the present design reusable for the future usage.
Example:
For example, in many real world situations we want to create only one instance of a class. For
example, there can be only one active president of country at a time regardless of personal
identity. This pattern is called Singleton pattern. Other software examples could be a
single DB connection shared by multiple objects as creating a separate DB connection for
every object may be costly. Similarly, there can be a single configuration manager or error
manager in an application that handles all problems instead of creating multiple managers.
91
Chapter 16. Design Patterns | Set 1 (Introduction)
These design patterns are about organizing different classes and objects to form larger
structures and provide new functionality.
Structural design patterns are Adapter, Bridge, Composite, Decorator, Facade, Flyweight,
Private Class Data and Proxy.
3. Behavioral
Behavioral patterns are about identifying common communication patterns between objects
and realize these patterns.
Behavioral patterns are Chain of responsibility, Command, Interpreter, Iterator, Mediator,
Memento, Null Object, Observer, State, Strategy, Template method, Visitor
References:
https://sourcemaking.com/design_patterns
https://sourcemaking.com/design_patterns/singleton
This article is contributed by Abhijit Saha. Please write comments if you find anything
incorrect, or you want to share more information about the topic discussed above.
Improved By : Sachin Araballi
Source
https://www.geeksforgeeks.org/design-patterns-set-1-introduction/
92
Chapter 17
93
Chapter 17. Design Patterns | Set 2 (Factory Method)
void printVehicle() {
cout << "I am two wheeler" << endl;
}
};
class FourWheeler : public Vehicle {
public:
void printVehicle() {
cout << "I am four wheeler" << endl;
}
};
// Client (or user) class
class Client {
public:
Client(int type) {
// Client explicitly creates classes according to type
if (type == 1)
pVehicle = new TwoWheeler();
else if (type == 2)
pVehicle = new FourWheeler();
else
pVehicle = NULL;
}
~Client() {
if (pVehicle)
{
delete[] pVehicle;
pVehicle = NULL;
}
}
Vehicle* getVehicle() {
return pVehicle;
}
private:
Vehicle *pVehicle;
};
// Driver program
int main() {
Client *pClient = new Client(1);
Vehicle * pVehicle = pClient->getVehicle();
pVehicle->printVehicle();
return 0;
}
94
Chapter 17. Design Patterns | Set 2 (Factory Method)
Output:
I am two wheeler
95
Chapter 17. Design Patterns | Set 2 (Factory Method)
// Factory method to create objects of different types.
// Change is required only in this function to create a new object type
Vehicle* Vehicle::Create(VehicleType type) {
if (type == VT_TwoWheeler)
return new TwoWheeler();
else if (type == VT_ThreeWheeler)
return new ThreeWheeler();
else if (type == VT_FourWheeler)
return new FourWheeler();
else return NULL;
}
// Client class
class Client {
public:
// Client doesn't explicitly create objects
// but passes type to factory method "Create()"
Client()
{
VehicleType type = VT_ThreeWheeler;
pVehicle = Vehicle::Create(type);
}
~Client() {
if (pVehicle) {
delete[] pVehicle;
pVehicle = NULL;
}
}
Vehicle* getVehicle() {
return pVehicle;
}
private:
Vehicle *pVehicle;
};
// Driver program
int main() {
Client *pClient = new Client();
Vehicle * pVehicle = pClient->getVehicle();
pVehicle->printVehicle();
return 0;
}
Output:
96
Chapter 17. Design Patterns | Set 2 (Factory Method)
I am three wheeler
In the above example, we have totally decoupled the selection of type for object creation
from Client. The library is now responsible to decide which object type to create based on
an input. Client just needs to make call to library’s factory Create method and pass the
type it wants without worrying about the actual implementation of creation of objects.
Thanks to Rumplestiltskin for providing above explanation.
1. Say, in a ‘Drawing’ system, depending on user’s input, different pictures like square,
rectangle, circle can be drawn. Here we can use factory method to create instances
depending on user’s input. For adding new type of shape, no need to change client’s
code.
2. Another example: In travel site, we can book train ticket as well bus tickets and flight
ticket. In this case user can give his travel type as ‘bus’, ‘train’ or ‘flight’.
Here we have an abstract class ‘AnyTravel’ with a static member function ‘GetObject’
which depending on user’s travel type, will create & return object of ‘BusTravel’ or
‘ TrainTravel’. ‘BusTravel’ or ‘ TrainTravel’ have common functions like passenger
name, Origin, destinationparameters.
Source
https://www.geeksforgeeks.org/design-patterns-set-2-factory-method/
97
Chapter 18
98
Chapter 18. Design Scalable System like Foursquare
99
Chapter 18. Design Scalable System like Foursquare
Dislike
Comment
CommentDate
** Notice that we can create UserID and PlaceID through Key Generation Service. KGS
ensures that each key is unique in the system. The second important point is that 8 byte is
enough to keep placeID and userID. Additionally, we can keep longitude and latitude with
8 bytes.
5- API Estimation
We can choose one of those two methods, REST or SOAP. There are four main API for
designing the system.
a-) AddPlace(api_dev_key, name, description, longitude, latitude, category): This API
returns the HTTP response showing operation is a success or not.
b-) DeletePlace(api_dev_key, placeID): This API returns the HTTP response showing
operation is a success or not.
c-) UpdatePlace(api_dev_key, placeID, name = null, description = null, longitude = null,
latitude = null, category = null)
d-) GetPlace(api_dev_key, search_query, maximum_return_count, radius, userlongi-
tude, userlatitude, categoryfilter = null, sortby, page_token): return JSON including the
list of most suitable places with their name, like count, dislike count, explanation, location,
thumbnail and etc…
6- System Design
You know how important indexing is in large scalable systems. There are no many up-
dating operations in our system. If we select the indexed pieces carefully, this system
responds faster. Indexing is not recommended for operations with too many updates. Our
search query should focus the user’s and places’ locations so the way to store data is so
important. There are three ways to store data which are SQL, Grid, Dynamic sizable grid.
SQL:
SQL is the simple solution. If you decide to use SQL, you need to index based on placeID,
longitude, and latitude. This method is not effective because there is two source to get the
data which are longitude and latitude. All these two sources are indexed sources. This is
a phase that reduces the response time because both of them return a response and these
coming responses combine for getting an optimum result.
select * from Places where placeLongitude between userLongitude – Radius and userLongi-
tude + Radius and placeLatitude between userLatitude – Radius and userLatitude + Radius
order by (selected ranking method) desc.
Grid:
We can use a grid to solve performance problems. We can divide the whole world into
smaller grids. This ensures that we can only deal with the few amount of grids. Thanks to
this method, we just only focus on the user location grid and its neighbour grids. A map is
the best choice to use a grid. Key is the gridID. Value is the whole places in this grid.
Select * from Places where Latitude between userLatitude – Radius and userLatitude +
Radius and GridID in (GridID1, GridID2, …)
100
Chapter 18. Design Scalable System like Foursquare
There is a problem with using a grid. As you know, all grids may not have same places so
we can use a dynamic sizable grid.
We can keep all index in memory. This provides system become closer to real-time experi-
ence.
Dynamic Sizable Grid:
A quadtree is a tree data structure in which each internal node has exactly four children. A
quadtree is a good option to use a dynamic sizable grid. The way to walk about the child
nodes of each grid can be provided by using doubly linked list. If we assume we want that
each grid cannot have over the 200 places, when the limit is exceeded and division begins.
7- Data Partitioning
We can partition the system based on regionID or locationID. If we use regionID, the system
may be non-uniform distributed. To illustrate this, It can be very difficult to fit popular
regions or regions having more places. We can handle this problem by using consistent
hashing. The second option to partition data is sharding based on locationID. If we use
locationID for sharding, the system becomes more uniformly distributed.
8- Replication and Load Balancer
As we mentioned above, the replication process is a valuable process to provide high avail-
ability, high reliability, and real-time experience. It is recommended to have 3 or more
replicas for each server and service. This ensures that system automatically becomes more
reliable and available. We can keep same data onto three different resources and thanks to
this process if one server dies, the system automatically continues to work replicas. One
more advantage of replication is the system may continue to run at an update to the system.
In replication process, there is a master-slave relationship between the server and its replicas.
A Master is for writing and reading operations; a slave is for reading operations.
Load balancing refers to distributing requests across a group of services and servers. When
we have talked about the replication and sharding, an incoming request must be directed
and this is done by a load balancer. We can use Round-Robin method to redirect incoming
requests but there may be a problem in this case. Round-Robin stops sending requests to
dead servers but Round-Robin cannot stop sending requests to a server exposed to heavy
traffic. We can prepare intelligent Round-Robin algorithm to take away this problem. Ad-
ditionally, we can use consistent hashing to redirect incoming requests. Consistent hashing
ensures that system becomes more uniformly distributed.
The possible problem for sharding with a load balancer is how we rebuilt the system when
this system dies. The possible approach is brute-force. This method is slow because we need
to rebuilt whole the system from the beginning. We can eliminate this problem by reverse
indexing. We can have another Index server that will hold all information about reverse
indexing. Reverse index maps all places. We need to build a map and key is the serverID
(QuadTree server) and value is all places.
9- Caching Mechanism
There are many caching methods that we can use but LRU is the good choice for this system.
Additionally, 80%-20% rule is valid for caching mechanism. This explains that we need to
memory capacity to 20% of daily data. Moreover, as we explained before, keeping indexes
in memory provides the system becomes more performance.
101
Chapter 18. Design Scalable System like Foursquare
102
Chapter 18. Design Scalable System like Foursquare
Reference: gorkemgenc.com
Source
https://www.geeksforgeeks.org/design-scalable-system-like-foursquare/
103
Chapter 19
104
Chapter 19. Design Scalable System like Instagram
105
Chapter 19. Design Scalable System like Instagram
5- DATABASE SCHEMA
As you know, we have talked about the pictures and users basically. We have to decide
whether to use SQL or NoSQL before defining database tables. We can use RDBMS to keep
data but as you know, scale process of a traditional database system is hard when we decide
to keep data o a traditional database system. On the other hand, if we use NoSQL, we can
scale system easily. There are three tables to store data;
User
– UserID : Int
– UserName : Nvarchar
– UserRealName : Nvarchar
– UserSurname : Nvarchar
– Mail : Nvarchar
– BirthDate : DateTime
– RegisterDate : DateTime
– LastLoginDate: DateTime
Picture
– PictureID : Int
– UserID : Int
– PicturePath : Nvarchar
– PictureLatitude : Int
– PictureLongitude : Int
– CreationDate : DateTime
UserFollow
– UserID1 : Int
– UserID2 : Int
** If we choose the NoSQL to keep data, we need to add a new table system. (PictureUser)
** We can store photos in S3 or HDFS.
** We can store all information about pictures with a key-value pair like Redis. Key is
pictureID, a value is other information about the picture. (For NoSQL)
** We can store all information about users with a key-value pair like Redis. Key is
userID, a value is other information about the user. (For NoSQL)
** We can use Cassandra, column-based data storage, to save follow-up of users.
Note: A lot of NoSQL database supports replication.
Note: We need to have an index on PictureID and CreationDate because we need to get
hottest pictures.
6- COMPONENT DESIGN
We can realize that uploading and downloading operation are not same. Uploading operation
is slower than downloading operation because uploading operation just based on disk. On
the other hand, read operation could be faster if we are using a cache.
If a user tries to upload a picture, he/she can consume all the connections. This causes to
when uploading operation continues, the system may not respond to read operation. If we
divide uploading and downloading operations into two separate services, then we can handle
this bottleneck. Notice that, web servers have connection limits at any time and we need
106
Chapter 19. Design Scalable System like Instagram
to focus on this point. Notice that, separating of uploading and downloading operations
ensure that system can be more scalable and optimize.
7- HIGH-LEVEL SYSTEM DESIGN
If we are designing a system, the basic concepts we need are;
– Client
– Services
– Web server
– Application server
– Picture Storage
– Database
– Caching
– Replication
– Redundancy
– Load balancing
– Sharding
There are two separate services in this system, which are upload image and download image.
Picture storage is used to keep pictures. A database is used to save all information about
users and pictures. (metadata). When a request comes to the system, the first to meet
request is the web servers. Web servers redirect an incoming request to application servers.
8- REPLICATION AND REDUNDANCY
Replication is a very important concept to provide availability and reliability. As we said,
Instagram should ensure that any files cannot be lost. Replication is a very important
concept to handle a failure of services or servers. Replication and redundancy basically mean
the copy of services or servers. We can replicate database servers, web servers, application
servers, image storages and etc.. Actually we can replicate all parts of the system. Notice
that replication also helps system to decrease response time. You imagine, if we divide
incoming requests into more resources rather than one resource, the system can easily meet
all incoming requests. In addition, the optimum number of a replica to each resource is 3
or more. Thanks to replications, if any server dies, the system continues to respond via
secondary resource.
9- DATA SHARDING
As you know, sharding is a very important concept which helps system to keep data into
different resources according to sharding process. There can be two sharding procedure to
use. First is partitioning based on UserID and second is partitioning based on PhotoID.
Partitioning based on UserID: We can divide incoming requests based on UserID. We will
find the shard number by UserID % number of shards. Conditioning with shard based on
UserID causes to problems. First is that system may be non-uniform distributed and second
is if a user is more active than the other user, the data of this user may not be fitted into one
resource. Another possible problem is handling the PictureID creation. PictureID should
be unique in the system, so each shard needs to have its creation policy.
Partitioning based on PictureID: We can divide incoming requests based on PictureID. We
will find the shard number by PictureID% number of shards. We can handle the non-uniform
distribution problem and popular user problem. We can easily create PhotoID with Key
107
Chapter 19. Design Scalable System like Instagram
Generation Service. Key Generation Service creates unique identifiers at first then serve
this unique identifier to incoming pictures. This helps us to handle PhotoID problem.
10- CACHING
Cache memory is a crucial part of reading data faster. Cache memory usage can base on
80-20 rule. This means that cache capacity is the 20% of the daily data size. We can use
LRU cache policy (Least Recently Used).
-CDN: CDN, Content Delivery Network is for distributed file cache servers. We can usage
CDN for keeping pictures.
-Memcache / Redis: Keep metadata in the cache with Memcache or Redis.
11- LOAD BALANCING
Load balancer allows incoming requests to be redirected to resources according to certain
criteria. We can use load balancer at every layer of the system.
– Between requests and web servers.
– Between web servers and application servers.
– Between application servers and databases
– Between application servers and image storages.
– Between application servers and cache databases.
– We can use Round Robin method for the load balancer. Round Robin method prevents
requests from going to dead servers but Round Robin method doesn’t deal with the situation
that any server is under heavy-traffic. We can modify Round Robin method to be a more
intelligent method to handle this problem.
12- DESIGN CONSIDERATION
Notice that we need to get the popular, latest and relevant photos of other people that
we follow. We can use a pre-generate timeline to decrease latency because you image that
system will fetch all friends of us firstly then fetch all pictures of our friends. After that,
the system will combine all pictures based on creation time, like count or other properties.
This action can take time and causes to late response. Pre-generate timeline keeps users’
timelines into a table previously. Thanks to the pre-generate timeline, the system can serve
them without the hassle of processing when they need to. What needs to be discussed here
is what to do when new data comes in. There are three important methods we can mention,
– Pull: The client asks if there are any changes at regular intervals. This creates some
problems. To illustrate this, we may not get new data when we use the system. Another
problem is most of the type, the client can encounter with the empty response.
– Push: In push method, a server can push new data to clients as soon as it is available.
Long Polling is one the best methods to use this method efficiently. Long polling is a method
that there is an open connection between the client and the server, and if any change occurs
about data, server return response to the client as soon as possible. This method may cause
a problem for users that follow a lot of users.
– Hybrid: Hybrid method is a combination of pull and push methods. Push method is
for users that follow few users and pull method is for users that follow a lot of users.
108
Chapter 19. Design Scalable System like Instagram
public class Server{
ArrayList<Machine> machines = new ArrayList<Machine>();
}
public class Storage{
ArrayList<StorageMachine> machines = new ArrayList<StorageMachine>();
}
public class Machine{
public ArrayList<User> users = new ArrayList<User>();
public int machineID;
}
public class StorageMachine{
public ArrayList<Picture> pictures = new ArrayList<Picture>();
public int machineID;
}
public class User{
private ArrayList<Integer> friends;
private ArrayList<Integer> pictures;
private int userID;
private int machineID;
private String information;
private Server server = new Server();
private Storage storage = new Storage();
public User(int userID, int machineID){
this.userID = userID;
this.machineID = machineID;
pictures = new ArrayList<Integer>();
friends = new ArrayList<Integer>();
}
public String getInformation() {
return information;
}
public void setInformation(String information){
this.information = information;
}
public getID(){
return userID;
}
public int getMachineID(){
return machineID;
}
public void addFriend(int id){
109
Chapter 19. Design Scalable System like Instagram
friends.add(i);
}
public void addPicture(int id){
pictures.add(i);
}
public int[] getFriends(){
int[] temp = new int[friends.size()];
for(int i=0; i<temp.length; i++){
temp[i] = friends.get(i);
}
return temp;
}
public int[] getPictures(){
int[] temp = new int[pictures.size()];
for(int i=0; i<temp.length; i++){
temp[i] = pictures.get(i);
}
return temp;
}
public User lookUpFriend(int machineID, int ID){
for(Machine m : server.machine){
if(m.machineID = machineID){
for(User user : m.users){
if(user.userID = ID){
return user;
}
}
}
}
return null;
}
public Picture lookUpPicture(int machineID, int ID){
for(StorageMachine m : storage.machine){
if(m.machineID = machineID){
for(Picture picture : m.pictures){
if(picture.pictureID = ID){
return picture;
}
}
}
}
return null;
}
110
Chapter 19. Design Scalable System like Instagram
}
public class Picture{
private int machineID;
private int pictureID;
private String photoPath;
public Picture(int machineID, int pictureID, String photoPath){
this.machineID = machineID;
this.pictureID = pictureID;
this.photoPath = photoPath;
}
public int getMachineID(){
return machineID;
}
public void setMachineID(int machineID){
this.machineID = machineID;
}
public int getPictureID(){
return pictureID;
}
public int getPhotoPath(){
return photoPath;
}
public void setPhotoPath(String photoPath){
this.photoPath = photoPath;
}
}
Reference: gorkemgenc.com
Source
https://www.geeksforgeeks.org/design-scalable-system-like-instagram/
111
Chapter 20
112
Chapter 20. Design Video Sharing System Like Youtube
– Users can like or dislike the videos, under this condition, the system should be kept a
number of likes, dislikes, comments, views to present these number to users.
b Non Functional Requirements
– System should be highly reliable. As we mentioned above, users can share and upload
videos to the system and system should guarantee to keep these videos against loss. Repli-
cation and sharding help system to keep data against loss. We will explain replication in
detail under step8.
– System should be highly available. If we talk about Youtube, Netflix or other large scal-
able systems, this means that these systems are going to be exposed to large traffic. There
can be a huge number of request at the same time at the system and system should tend to
respond to all requests in a real-time experience. Replication, sharding and load balancer
helps system to be highly available. We will talk about all three features under step7, step8
and step9.
– System should respond with minimum latency. You imagine that you would be a user
and you want to do anything on the Youtube, do you really want to wait to much time to
get the response? I think you don’t want to wait too much time as well as you want to
experience the system with real-time. To illustrate this, when you search a video in the
system, this system should suggest related videos as soon as possible.
c- Extended Requirements
– Extended requirements mainly for improving system performance. To illustrate this, the
system can be monitored to define usability and durability.
3- CAPACITY ESTIMATION
To estimate the capacity of the scalable system, we should focus on the total user, active user,
number of coming request and data. All these features can be considered for next 10 years
because when we do that, we can easily handle the scaling problems that we can encounter
after years. We can estimate the storage capacity, total usage, bandwidth estimate, cache
capacity etc.. before starting the design process. Notice that, at systems like Youtube, the
number of the read and write operations can be different. For example, in our system, we
can estimate that the ratio of the write and read operation is 1:100. This is briefly called
an upload and download ratio.
a- User estimation
We can assume that total user count of the Youtube is 1 billion and Youtube has 500 million
active users in each day.
b- Storage estimation
Let’s talk about the average size of videos. We are just focusing on the uploading operations
because only uploading operations take up space at the storage. We assume one minute of
video needs 50 MB to storage. This also should include the multiple formats of the uploaded
video. Let’s try to find the total minute of uploaded video in each day. As you remember,
we have mentioned the ratio of write and read operation which is 1:100. If we assume that
each active user downloads 5 videos then the number of uploaded video is 500 Million * 5 /
100 = 25 Million. We can assume that each video is approximately 2 minute. A total video
minute is 50 Million in each day. As a result, daily needed storage approximately equals
to 50 Million * 50 MB = 125 * 10^6 * 50 * 10^6 = 125 * 50 * 10^12 = 6250 * 10^12 =
113
Chapter 20. Design Video Sharing System Like Youtube
6250 TB. This amount of daily storage is huge. Note that, this calculation doesn’t contain
the replication, duplication and etc… and because of that the storage estimation can change
and may be overestimated.
c- Cache Estimation
Cache is the fast way to catch the data. I have explained the cache system in this
link Caching. You can get the detail information of the cache system.
In general, a cache is estimated according to daily storage. To estimate the cache capacity,
we use the popular ratio based on the 80-20 rule. This means 20% of popular data are
cached. Briefly, we can estimate that Youtube cache capacity approximately equals to 6250
TB / 5 = 1250 TB.
d- Bandwith Estimation
An internet connection with a larger bandwidth can move a set amount of data much faster
than an internet connection with a lower bandwidth. Bandwith is calculated according to
both of download and upload operations for a second.
4- DATABASE DESIGN
There are two choices to define the database schema. These are SQL and NoSQL. We can
use traditional database management system like MsSQL or MySQL to keep data. As you
know, we should keep information about videos and users into RDBMS. Other information
about videos, called metadata, should be kept too. Now we have main three tables to keep
data. (Notice that we just only think the basic properties of Youtube. We can forget the
recommendation system).
User
– UserID (primary key)
– Name (nvarchar)
– Age (Integer)
– Email (nvarchar)
– Adress (nvarchar)
– Register Date (DateTime)
– Last Login (DateTime)
Video
– VideoID (primary key – generated by KGS – Key generation service)
– VideoTitle (nvarchar)
– Size (float)
– UserID (foreign key with User Table)
– Description (nvarchar)
– CategoryID (int) : Note that we can create Category Table to define categories
– Number of Likes (int)
– Number of Dislikes (int)
– Number of Displayed (int) – We can use big int to keep displayed number
– Uploaded Date (DateT?me)
VideoComment
– CommentID (primary key)
– UserID (foreign key with User Table)
114
Chapter 20. Design Video Sharing System Like Youtube
115
Chapter 20. Design Video Sharing System Like Youtube
service, search service or download service. If we give an opportunity to users that download
video asynchronously, system traffic will be relaxed. An encoder is to encode uploaded
video into multiple formats. There are three types of databases which are Video content
database, user database and video metadata storage. Queue process takes place between an
application server and encode.
Our Youtube service would be read-heavy and we should be careful when building a system.
Our main goal should be returning videos quickly. We can keep copies of videos on different
servers to handle the traffic problems. Additionally, this ensures the safety of this system.
We can distribute our traffic to different servers with using a load balancer. The system can
keep two more replicas of metadata database, user database and video content database. We
can use CDN to cache the popular data.
Flow diagram of the system;
1- A client sends a request.
2- Request meets from the web server.
3- Web server controls the cache. There can be two more cache databases on the system.
4- If the request takes place into a cache, a response is redirected to the client.
5- Otherwise, web server redirects the request an to the application server.
6- There can be load balancer between web servers and application servers.
** If this request is search or view service, it tries to find the request by looking at the
metadata database and the video content database. A load balancer can be placed each
layer of the system such as between application server and video content database, between
the application server and metadata database etc…
When a server responds the request to the client, related data is cached according to the
cache process.
7- OPTIMIZATION OF THE SYSTEM
As we know, Youtube is huge video sharing system. Users can upload videos and the number
of uploading videos grows exponentially day by day. According to uploading videos, there
may one more same video in the system. To eliminate the duplication of videos we can
implement an intelligent algorithm. For example, when a video comes to a system, the
algorithm automatically checks whether this video is already kept in the system or not. If
the system has already this video, then we don’t need to keep duplicate data. It saves us
from unnecessary use of space. Additionally, if incoming video includes a video kept in the
system, then we can divide videos into small chunks and we just give the only reference to
same video chunks to handle the duplication problem.
8- SHARDING PROCESS
As we mentioned above, sharding ensures that the system is more reliable and available.
There can be two types of sharding process which are userID based and videoID based.
Notice that userID based sharding process may cause problems. To illustrate this, when
we shard data according to userID, the system tries to fit all user data in one machine and
if the user uploads huge video to Youtube, we can face a fit problem. Another problem
for sharding based on userID is a uniform distribution. Not every person uploads the same
data size. This causes that system can be distributed non-uniformly. When we shard data
according to videoID, we can eliminate these two problems.
9- REPLICATION PROCESS
116
Chapter 20. Design Video Sharing System Like Youtube
As we mentioned above, the replication process is a valuable process to provide high avail-
ability, high reliability and real-time experience. As you know, it is recommended to have
3 or more replicas for each server and service. If our system has 2 more replicas for an
application server, web server, CDN, encoding service, user database, metadata database,
cache DB, system automatically becomes more reliable and available. We can keep same
data onto three different resources and thanks to this process if one server dies, the system
automatically continues to work replicas. One more advantage of replication is the system
may continue to run at an update to the system.
10- LOAD BALANCING
Load balancing refers to distributing requests across a group of services and servers. When
we have talked about the replication and sharding, an incoming request must be directed
and this is done by a load balancer. We can use Round-Robin method to redirect incoming
requests but there may be a problem in this case. Round-Robin stops sending requests to
dead servers but Round-Robin cannot stop sending requests to a server exposed to heavy
traffic. We can prepare intelligent Round-Robin algorithm to take away this problem. Ad-
ditionally, we can use consistent hashing to redirect incoming requests. Consistent hashing
ensures that system becomes more uniformly distributed.
11- CACHING MECHANISM
There can be two types of caching mechanism in our system.
– Memcache (for user and video database)
– CDN (for static media)
** Memcache or Redis is the good way to create cache mechanism. As you know, caching
ensures that system responds popular data quickly. Additionally, we can set up a caching
mechanism on same servers or different servers. We should be careful about caching mech-
anism because when we keep cache data on the same server, this means we have limited
capacity. On the other hand, when we set cache mechanism on different servers we have
more capacity. However, using a different server for caching mechanism cause latency prob-
lem. LRU principle is a good way to set up a cache mechanism.
12- DESIGN CONSIDERATION
Video uploading is a big process. When it fails, the system should ensure that it should
continue to upload video from the failing point.
Video encoding process should include the queue operations. When an uploaded video
comes, this new task is added to a queue and all tasks in the queue are taken one by one
from a queue.
13-) BASIC CODING OF SYSTEM
117
Chapter 20. Design Video Sharing System Like Youtube
public class Machine {
public ArrayList<User> users = new ArrayList<User>();
public int machineID;
}
public class VideoMachine {
public ArrayList<Video> videos = new ArrayList<Video>();
public int machineID;
}
public class User {
private ArrayList<Integer> videos;
private int userID;
private int machineID;
private String information;
private Server server = new Server();
private Storage storage = new Storage();
public User(int userID, int machineID) {
this.userID = userID;
this.machineID = machineID;
videos = new ArrayList<Integer>();
}
public String getInformation() { return information; }
public void setInformation(String information) {
this.information = information;
}
public getID() { return userID; }
public int getMachineID() { return machineID; }
public void addV ? deo(int id) { videos.add(i); }
public int[] getVideos() {
int[] temp = new int[videos.size()];
for (int i = 0; i < temp.length; i++) {
temp[i] = videos.get(i);
}
return temp;
}
public User lookUpVideo(int machineID, int ID) {
for (Machine m : storage.machine) {
if (m.machineID = machineID) {
118
Chapter 20. Design Video Sharing System Like Youtube
Reference: gorkemgenc.com
Source
https://www.geeksforgeeks.org/design-video-sharing-system-like-youtube/
119
Chapter 21
• User
• Movie
• Theater
• Booking
• Address
• Facilities
120
Chapter 21. Design a movie ticket booking system like Bookmyshow
public enum MovieType {
ENGLISH,
HINDI;
}
public enum SeatType {
NORMAL,
EXECUTIVE,
PREMIUM,
VIP;
}
public enum PaymentStatus {
PAID,
UNPAID;
}
class User {
int userId;
String name;
Date dateOfBirth;
String mobNo;
String emailId;
String sex;
}
class Movie {
int movieId;
int theaterId;
MovieType movieType;
MovieStatus movieStatus;
}
class Theater {
int theaterId;
String theaterName;
Adress adress;
List<Movie> movies;
float rating;
}
class Booking {
int bookingId;
121
Chapter 21. Design a movie ticket booking system like Bookmyshow
int userId;
int movieId;
List<Movie> bookedSeats;
int amount;
PaymentStatus status_of_payment;
Date booked_date;
Time movie_timing;
}
class Address {
String city;
String pinCode;
String state;
String streetNo;
String landmark;
}
This is an OOP design question, so full code is not required. The above code has classes
and attributes only. In the above code, as you can see enums are self-explanatory.
We have users class in which users details are kept.
Theater class in which name of the theater, it’s address and list of movies currently running
are kept.
Booking class lets you book the seat in a particular theater. It keeps a reference in Movie,
Payment class.
How to handle the cases where two persons are trying to access the same seat
almost same time?
Lets take SeatBook and Transactions class which will be called from the main class. Here
from the above code, We expand a bit the payment process which is not shown in the above
code. In SeatBook class we will have reference to Transaction class also.
Now to ensure when two persons are trying to access the same seat almost at the same time
then we would use Synchronized method of Thread class and will call a thread belong to
each log in user.
Class SeatBook
{
Transaction transaction_obj;
bool seats[total_seats];
String place;
String ticketType;
bool check_availability();
int position_of_seat()
{
return seat_pos_in_theator;
122
Chapter 21. Design a movie ticket booking system like Bookmyshow
}
void multiple tickets();
void final_booking()
{
place = positon_of_seat();
if (single_ticket)
continue;
else
mutliple_ticket_booking();
Transaction_obj.pay(ticketType, seats_booked, place);
}
}
Source
https://www.geeksforgeeks.org/design-movie-ticket-booking-system-like-bookmyshow/
123
Chapter 22
import java.util.HashMap;
/*
* This class represents the system
124
Chapter 22. Design an online book reader system
*/
class OnlineReaderSystem {
private Library library;
private UserManager userManager;
private Display display;
private Book activeBook;
private User activeUser;
public OnlineReaderSystem()
{
userManager = new UserManager();
library = new Library();
display = new Display();
}
public Library getLibrary()
{
return library;
}
public UserManager getUserManager()
{
return userManager;
}
public Display getDisplay()
{
return display;
}
public Book getActiveBook()
{
return activeBook;
}
public void setActiveBook(Book book)
{
activeBook = book;
display.displayBook(book);
}
public User getActiveUser()
{
return activeUser;
}
public void setActiveUser(User user)
125
Chapter 22. Design an online book reader system
{
activeUser = user;
display.displayUser(user);
}
}
/*
* We then implement separate classes to handle the user
* manager, the library, and the display components
*/
/*
* This class represents the Library which is responsible
* for storing and searching the books.
*/
class Library {
private HashMap<Integer, Book> books;
public Library()
{
books = new HashMap<Integer, Book>();
}
public Boolean addBook(int id, String details, String title)
{
if (books.containsKey(id)) {
return false;
}
Book book = new Book(id, details, title);
books.put(id, book);
return true;
}
public Boolean addBook(Book book)
{
if (books.containsKey(book.getId())) {
return false;
}
books.put(book.getId(), book);
return true;
}
public boolean remove(Book b)
{
return remove(b.getId());
}
126
Chapter 22. Design an online book reader system
127
Chapter 22. Design an online book reader system
{
return remove(u.getId());
}
public boolean remove(int id)
{
if (users.containsKey(id)) {
return false;
}
users.remove(id);
return true;
}
public User find(int id)
{
return users.get(id);
}
}
/*
* This class represents the Display, which is responsible
* for displaying the book, it's pages and contents. It also
* shows the current user. * It provides the method
* turnPageForward, turnPageBackward, refreshPage etc.
*/
class Display {
private Book activeBook;
private User activeUser;
private int pageNumber = 0;
public void displayUser(User user)
{
activeUser = user;
refreshUsername();
}
public void displayBook(Book book)
{
pageNumber = 0;
activeBook = book;
refreshTitle();
refreshDetails();
refreshPage();
}
public void turnPageForward()
128
Chapter 22. Design an online book reader system
{
pageNumber++;
System.out.println("Turning forward to page no " +
pageNumber + " of book having title " +
activeBook.getTitle());
refreshPage();
}
public void turnPageBackward()
{
pageNumber--;
System.out.println("Turning backward to page no " +
pageNumber + " of book having title " +
activeBook.getTitle());
refreshPage();
}
public void refreshUsername()
{
/* updates username display */
System.out.println("User name " + activeUser.getName() +
" is refreshed");
}
public void refreshTitle()
{
/* updates title display */
System.out.println("Title of the book " +
activeBook.getTitle() + " refreshed");
}
public void refreshDetails()
{
/* updates details display */
System.out.println("Details of the book " +
activeBook.getTitle() + " refreshed");
}
public void refreshPage()
{
/* updated page display */
System.out.println("Page no " + pageNumber + " refreshed");
}
}
/*
* The classes for User and Book simply hold data and
* provide little functionality.
129
Chapter 22. Design an online book reader system
130
Chapter 22. Design an online book reader system
*/
class User {
private int userId;
private String name;
private String details;
public void renewMembership()
{
}
public User(int id, String details, String name)
{
this.userId = id;
this.details = details;
this.name = name;
}
public int getId()
{
return userId;
}
public void setId(int id)
{
userId = id;
}
public String getDetails()
{
return details;
}
public void setDetails(String details)
{
this.details = details;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
131
Chapter 22. Design an online book reader system
// This class is used to test the Application
public class AppTest {
public static void main(String[] args)
{
OnlineReaderSystem onlineReaderSystem = new OnlineReaderSystem();
Book dsBook = new Book(1, "It contains Data Structures", "Ds");
Book algoBook = new Book(2, "It contains Algorithms", "Algo");
onlineReaderSystem.getLibrary().addBook(dsBook);
onlineReaderSystem.getLibrary().addBook(algoBook);
User user1 = new User(1, " ", "Ram");
User user2 = new User(2, " ", "Gopal");
onlineReaderSystem.getUserManager().addUser(user1);
onlineReaderSystem.getUserManager().addUser(user2);
onlineReaderSystem.setActiveBook(algoBook);
onlineReaderSystem.setActiveUser(user1);
onlineReaderSystem.getDisplay().turnPageForward();
onlineReaderSystem.getDisplay().turnPageForward();
onlineReaderSystem.getDisplay().turnPageBackward();
}
}
Reference :
Book :Cracking the Coding Interview
Source
https://www.geeksforgeeks.org/design-an-online-book-reader-system/
132
Chapter 23
133
Chapter 23. Design an online hotel booking system like OYO Rooms
public enum PaymentStatus {
PAID,
UNPAID;
}
public enum Facility {
LIFT;
POWER_BACKUP;
HOT_WATERR;
BREAKFAST_FREE;
SWIMMING_POOL;
}
class User {
int userId;
String name;
Date dateOfBirth;
String mobNo;
String emailId;
String sex;
}
// For the room in any hotel
class Room {
int roomId; // roomNo
int hotelId;
RoomType roomType;
RoomStatus roomStatus;
}
class Hotel {
int hotelId;
String hotelName;
Adress adress;
// hotel contains the list of rooms
List<Room> rooms;
float rating;
Facilities facilities;
}
// a new booking is created for each booking
// done by any user
class Booking {
134
Chapter 23. Design an online hotel booking system like OYO Rooms
int bookingId;
int userId;
int hotelId;
// We are assuming that in a single
// booking we can book only the rooms
// of a single hotel
List<Rooms> bookedRooms;
int amount;
PaymentStatus status_of_payment;
Date bookingTime;
Duration duration;
}
class Address {
String city;
String pinCode;
String state;
String streetNo;
String landmark;
}
class Duration {
Date from;
Date to;
}
class Facilities {
List<Facility> facilitiesList;
}
Let me explain about the classes and the relationships among themselves.
The enums defined here are self-explanatory. The classes User, Room, and Address also self-
explanatory. The class Facilities contains a list of facilities (enum) that the hotel provides.
We can add more facilities in the Facility enum if required. The duration class has two
attributes “from” and “to”, which is obvious.
Now, the class “Hotel” contains:
1. List of rooms (Room class) // this is the list of rooms the hotel has
2. Address class // its address
3. Facilities class // the facilities it has
The class “Booking” contains:
1. User // information about the
135
Chapter 23. Design an online hotel booking system like OYO Rooms
Source
https://www.geeksforgeeks.org/design-online-hotel-booking-system-like-oyo-rooms/
136
Chapter 24
Design data structures and algorithms for in-memory file system - GeeksforGeeks
Explain the data structures and algorithms that you would use to design an in-memory file
system. Illustrate with an example in the code logic where possible.
Asked In: Amazon
A file system, in its most simplistic version, consists of Files and Directories. Each Di-
rectory contains a set of Files and Directories. Since Files and Directories share so many
characteristics, we’ve implemented them such that they inherit from the same class, Entry.
Implemented Main logic in Java
137
Chapter 24. Design data structures and algorithms for in-memory file system
}
public boolean delete()
{
if (parent == null)
return false;
return parent.deleteEntry(this);
}
public abstract int size();
/* Getters and setters. */
public long getcreationTime()
{
return created;
}
public long getLastUpdatedTime()
{
return lastUpdated;
}
public long getLastAccessedTime()
{
return lastAccessed;
}
public void changeName(String n)
{
name = n;
}
public String getName()
{
return name;
}
}
// A class to represent a File (Inherits
// from Entry)
public class File extends Entry
{
private String content;
private int size;
public File(String n, Directory p, int sz)
{
super(n, p);
size = sz;
}
public int size()
{
138
Chapter 24. Design data structures and algorithms for in-memory file system
return size;
}
public String getContents()
{
return content;
}
public void setContents(String c)
{
content = c;
}
}
// A class to represent a Directory (Inherits
// from Entry)
public class Directory extends Entry
{
protected Arraylist<Entry> contents;
public Directory(String n, Directory p)
{
super(n, p);
contents = new Arraylist<Entry>();
}
public int size()
{
int size = 0;
for (Entry e : contents)
size += e.size();
return size;
}
public int numberOfFiles()
{
int count = 0;
for (Entry e : contents)
{
if (e instanceof Directory)
{
count++; // Directory counts as a file
Directory d = (Directory) e;
count += d. numberOfFiles ();
}
else if (e instanceof File)
count++;
}
return count;
}
139
Chapter 24. Design data structures and algorithms for in-memory file system
Alternatively, we could have implemented Directory such that it contains separate lists for
files and subdirectories. This makes the nurnberOfFiles () method a bit cleaner, since it
doesn’t need to use the instanceof operator, but it does prohibit us from cleanly sorting files
and directories by dates or names.
For data block allocation, we can use bitmask vector and linear search (see “Practical File
System Design”) or B+ trees (see Reference or Wikipedia).
References:
https://www.careercup.com/question?id=13618661
https://stackoverflow.com/questions/14126575/data-structures-used-to-build-file-systems
Source
https://www.geeksforgeeks.org/design-data-structures-algorithms-memory-file-system/
140
Chapter 25
Design the Data Structures(classes and objects)for a generic deck of cards - GeeksforGeeks
Design the data structures for a generic deck of cards Explain how you would sub-class
it to implement particular card games and how you would subclass the data structures to
implement blackjack.
Asked In : Amazon Interview
Solution:
First, we need to recognize that a “generic” deck of cards can mean many things. Generic
could mean a standard deck of cards that can play a poker-like game, or it could even stretch
to Uno or Baseball cards.
To implement particular card games
Let’s assume that the deck is a standard 52-card set like you might see used in a blackjack
or poker game. If so, the design might look like this:
The structure is clear here: a deck contains four suits and a suit contains 13 card. Each
card has a numerical value from 1 to 13. If you think about a card game, different games
differ from ways of dealing cards and putting cards back in. So we can have a set of abstract
methods inside the class ‘Deck’ to allow sub-class implements its own way of dealing. The
class diagram I draw is here:
141
Chapter 25. Design the Data Structures(classes and objects)for a generic deck of cards
142
Chapter 25. Design the Data Structures(classes and objects)for a generic deck of cards
return v;
return 10;
}
BlackJackCard(int val, SUIT::Enum suit)
: Card(val, suit){};
};
class player {
private:
int id;
int bet;
set<int> points;
vector<BlackJackCard*> bjcs;
bool addPoint(set<int>& amp; points, BlackJackCard * card)
{
if (points.empty()) {
points.insert(card->val());
if (card->val() == 1)
points.insert(11);
} else {
/* Set elements are ALWAYS CONST, they can't
be modified once inserted. */
set<int> tmp;
for (auto it = points.begin(); it != points.end(); ++it) {
tmp.insert(*it + card->val());
if (card->val() == 1)
tmp.insert(*it + 11);
}
points = tmp;
}
}
void getPoints()
{
cout << "You All Possible Points : " << endl;
for (auto it = points.begin(); it != points.end(); ++it) {
cout << *it << endl;
}
};
int getMinPoints()
{
/* set is implemented by commonly BST, so else
are in order!!!
learn to use lower_bound() and upper_bound()
"they allow the direct iteration on subsets
143
Chapter 25. Design the Data Structures(classes and objects)for a generic deck of cards
144
Chapter 25. Design the Data Structures(classes and objects)for a generic deck of cards
{
for (auto it = bjcs.begin(); it != bjcs.end(); ++it) {
delete *it;
}
};
};
// Driver code
int main()
{
srand(time(NULL));
player p(1, 1000);
p.getAnotherCard();
p.getAnotherCard();
p.getAnotherCard();
return 0;
}
Output:
You Cards :
10
10
You All Possible Points :
20
Stand
To implement Blackjack.
Note: Now, let’s say we’re building a blackjack game, so we need to know the value of the
cards. Face cards are 10 and an ace is 11 (most of the time, but that’s the job of the Hand
class, not the following class).
At the start of a blackjack game, the players and the dealer receive two cards each. The
players’ cards are normally dealt face up, while the dealer has one face down (called the
hole card) and one face up.
The best possible blackjack hand is an opening deal of an ace with any ten-point card. This
is called a “blackjack”, or a natural 21, and the player holding this automatically wins unless
the dealer also has a blackjack. If a player and the dealer each have a blackjack, the result
is a push for that player. If the dealer has a blackjack, all players not holding a blackjack
lose.
145
Chapter 25. Design the Data Structures(classes and objects)for a generic deck of cards
146
Chapter 25. Design the Data Structures(classes and objects)for a generic deck of cards
return value();
}
public int maxValue()
{
if (isAce())
return 11;
else
return value();
}
public boolean isAce()
{
return faceValue == 1;
}
public boolean isFaceCard()
{
return faceValue >= 11 & amp;
&faceValue <= 13;
}
}
/* This is just one way of handling aces. We could, alternatively, create a class
of type Ace that extends BlackJackCard. */
References :
https://www.careercup.com/question?id=2983
http://stackoverflow.com/questions/37363008/a-singleton-class-to-design-a-generic-deck-of-card
Source
https://www.geeksforgeeks.org/design-data-structuresclasses-objectsfor-generic-deck-cards/
147
Chapter 26
148
Chapter 26. Facade Design Pattern | Introduction
package structural.facade;
public interface Hotel
{
public Menus getMenus();
}
149
Chapter 26. Facade Design Pattern | Introduction
package structural.facade;
public class NonVegRestaurant implements Hotel
{
public Menus getMenus()
{
NonVegMenu nv = new NonVegMenu();
return nv;
}
}
VegRestaurant.java
package structural.facade;
public class VegRestaurant implements Hotel
{
public Menus getMenus()
{
VegMenu v = new VegMenu();
return v;
}
}
VegNonBothRestaurant.java
package structural.facade;
public class VegNonBothRestaurant implements Hotel
{
public Menus getMenus()
{
Both b = new Both();
return b;
}
}
package structural.facade;
public class HotelKeeper
{
public VegMenu getVegMenu()
{
150
Chapter 26. Facade Design Pattern | Introduction
From this, It is clear that the complex implementation will be done by HotelKeeper himself.
The client will just access the HotelKeeper and ask for either Veg, NonVeg or VegNon Both
Restaurant menu.
How will the client program access this façade?
package structural.facade;
public class Client
{
public static void main (String[] args)
{
HotelKeeper keeper = new HotelKeeper();
VegMenu v = keeper.getVegMenu();
NonVegMenu nv = keeper.getNonVegMenu();
Both = keeper.getVegNonMenu();
}
}
In this way the implementation is sent to the façade. The client is given just one interface
and can access only that. This hides all the complexities.
When Should this pattern be used?
The facade pattern is appropriate when you have a complex system that you want to
expose to clients in a simplified way, or you want to make an external communication layer
over an existing system which is incompatible with the system. Facade deals with interfaces,
151
Chapter 26. Facade Design Pattern | Introduction
not implementation. Its purpose is to hide internal complexity behind a single interface that
appears simple on the outside.
Source
https://www.geeksforgeeks.org/facade-design-pattern-introduction/
152
Chapter 27
• Less number of objects reduces the memory usage, and it manages to keep us away
from errors related to memory like java.lang.OutOfMemoryError.
• Although creating an object in Java is really fast, we can still reduce the execution
time of our program by sharing objects.
In Flyweight pattern we use a HashMap that stores reference to the object which have
already been created, every object is associated with a key. Now when a client wants to
create an object, he simply has to pass a key associated with it and if the object has already
been created we simply get the reference to that object else it creates a new object and then
returns it reference to the client.
Intrinsic and Extrinsic States
To understand Intrinsic and Extrinsic state, let us consider an example.
Suppose in a text editor when we enter a character, an object of Character class is created,
the attributes of the Character class are {name, font, size}. We do not need to create an
object every time client enters a character since letter ‘B’ is no different from another ‘B’ .
If client again types a ‘B’ we simply return the object which we have already created before.
Now all these are intrinsic states (name, font, size), since they can be shared among the
different objects as they are similar to each other.
Now we add to more attributes to the Character class, they are row and column. They
specify the position of a character in the document. Now these attributes will not be similar
153
Chapter 27. Flyweight Design Pattern
even for same characters, since no two characters will have the same position in a document,
these states are termed as extrinsic states, and they can’t be shared among objects.
Implementation : We implement the creation of Terrorists and Counter Terrorists In the
game of Counter Strike. So we have 2 classes one for Terrorist(T) and other for Counter
Terrorist(CT). Whenever a player asks for a weapon we assign him the asked weapon. In
the mission, terrorist’s task is to plant a bomb while the counter terrorists have to diffuse
the bomb.
Why to use Flyweight Design Pattern in this example? Here we use the Fly Weight
design pattern, since here we need to reduce the object count for players. Now we have n
number of players playing CS 1.6, if we do not follow the Fly Weight Design Pattern then
we will have to create n number of objects, one for each player. But now we will only have to
create 2 objects one for terrorists and other for counter terrorists, we will reuse then again
and again whenever required.
Intrinsic State : Here ‘task’ is an intrinsic state for both types of players, since this is
always same for T’s/CT’s. We can have some other states like their color or any other
properties which are similar for all the Terrorists/Counter Terrorists in their respective
Terrorists/Counter Terrorists class.
Extrinsic State : Weapon is an extrinsic state since each player can carry any weapon of
his/her choice. Weapon need to be passed as a parameter by the client itself.
Class Diagram :
154
Chapter 27. Flyweight Design Pattern
155
Chapter 27. Flyweight Design Pattern
{
TASK = "DIFFUSE BOMB";
}
public void assignWeapon(String weapon)
{
this.weapon = weapon;
}
public void mission()
{
System.out.println("Counter Terrorist with weapon "
+ weapon + "|" + " Task is " + TASK);
}
}
// Claass used to get a playeer using HashMap (Returns
// an existing player if a player of given type exists.
// Else creates a new player and returns it.
class PlayerFactory
{
/* HashMap stores the reference to the object
of Terrorist(TS) or CounterTerrorist(CT). */
private static HashMap <String, Player> hm =
new HashMap<String, Player>();
// Method to get a player
public static Player getPlayer(String type)
{
Player p = null;
/* If an object for TS or CT has already been
created simply return its reference */
if (hm.containsKey(type))
p = hm.get(type);
else
{
/* create an object of TS/CT */
switch(type)
{
case "Terrorist":
System.out.println("Terrorist Created");
p = new Terrorist();
break;
case "CounterTerrorist":
System.out.println("Counter Terrorist Created");
p = new CounterTerrorist();
break;
default :
System.out.println("Unreachable code!");
156
Chapter 27. Flyweight Design Pattern
}
// Once created insert it into the HashMap
hm.put(type, p);
}
return p;
}
}
// Driver class
public class CounterStrike
{
// All player types and weopons (used by getRandPlayerType()
// and getRandWeapon()
private static String[] playerType =
{"Terrorist", "CounterTerrorist"};
private static String[] weapons =
{"AK-47", "Maverick", "Gut Knife", "Desert Eagle"};
// Driver code
public static void main(String args[])
{
/* Assume that we have a total of 10 players
in the game. */
for (int i = 0; i < 10; i++)
{
/* getPlayer() is called simply using the class
name since the method is a static one */
Player p = PlayerFactory.getPlayer(getRandPlayerType());
/* Assign a weapon chosen randomly uniformly
from the weapon array */
p.assignWeapon(getRandWeapon());
// Send this player on a mission
p.mission();
}
}
// Utility methods to get a random player type and
// weapon
public static String getRandPlayerType()
{
Random r = new Random();
// Will return an integer between [0,2)
int randInt = r.nextInt(playerType.length);
157
Chapter 27. Flyweight Design Pattern
// return the player stored at index 'randInt'
return playerType[randInt];
}
public static String getRandWeapon()
{
Random r = new Random();
// Will return an integer between [0,5)
int randInt = r.nextInt(weapons.length);
// Return the weapon stored at index 'randInt'
return weapons[randInt];
}
}
Output:
References:
Source
https://www.geeksforgeeks.org/flyweight-design-pattern/
158
Chapter 28
Design components
• Controller : The controller is the initial contact point for handling all requests in
the system. The controller may delegate to a helper to complete authentication and
authorization of a user or to initiate contact retrieval.
• View: A view represents and displays information to the client. The view retrieves
information from a model. Helpers support views by encapsulating and adapting the
underlying data model for use in the display.
• Dispatcher: A dispatcher is responsible for view management and navigation, man-
aging the choice of the next view to present to the user, and providing the mechanism
for vectoring control to this resource.
159
Chapter 28. Front Controller Design Pattern
• Helper : A helper is responsible for helping a view or controller complete its process-
ing. Thus, helpers have numerous responsibilities, including gathering data required
by the view and storing this intermediate model, in which case the helper is sometimes
referred to as a value bean.
class TeacherView
{
public void display()
{
System.out.println("Teacher View");
}
}
class StudentView
{
public void display()
{
System.out.println("Student View");
}
}
class Dispatching
{
private StudentView studentView;
private TeacherView teacherView;
public Dispatching()
{
studentView = new StudentView();
teacherView = new TeacherView();
}
public void dispatch(String request)
{
if(request.equalsIgnoreCase("Student"))
{
studentView.display();
}
else
{
teacherView.display();
}
}
}
class FrontController
160
Chapter 28. Front Controller Design Pattern
{
private Dispatching Dispatching;
public FrontController()
{
Dispatching = new Dispatching();
}
private boolean isAuthenticUser()
{
System.out.println("Authentication successfull.");
return true;
}
private void trackRequest(String request)
{
System.out.println("Requested View: " + request);
}
public void dispatchRequest(String request)
{
trackRequest(request);
if(isAuthenticUser())
{
Dispatching.dispatch(request);
}
}
}
class FrontControllerPattern
{
public static void main(String[] args)
{
FrontController frontController = new FrontController();
frontController.dispatchRequest("Teacher");
frontController.dispatchRequest("Student");
}
}
Output:
161
Chapter 28. Front Controller Design Pattern
Student View
Advantages :
• Centralized control : Front controller handles all the requests to the Web applica-
tion. This implementation of centralized control that avoids using multiple controllers
is desirable for enforcing application-wide policies such as users tracking and security.
• Thread-safety : A new command object arises when receiving a new request and
the command objects are not meant to be thread safe. Thus, it will be safe in the
command classes. Though safety is not guaranteed when threading issues are gathered,
codes that act with command is still thread safe.
Disadvantages :
Source
https://www.geeksforgeeks.org/front-controller-design-pattern/
162
Chapter 29
163
Chapter 29. How to design a parking lot using object-oriented principles?
164
Chapter 29. How to design a parking lot using object-oriented principles?
spotsNeeded = 1;
size = VehicleSize.Compact;
}
/* Checks if the spot is a Compact or a Large. */
public boolean canFitinSpot(ParkingSpot spot)
{ ... }
}
public class Motorcycle extends Vehicle
{
public Motorcycle()
{
spotsNeeded = 1;
size = VehicleSize.Motorcycle;
}
public boolean canFitinSpot(ParkingSpot spot)
{ ... }
}
The ParkingSpot is implemented by having just a variable which represents the size of the
spot. We could have implemented this by having classes for LargeSpot, CompactSpot, and
MotorcycleSpot which inherit from ParkingSpot, but this is probably overkilled. The spots
probably do not have different behaviors, other than their sizes.
165
Chapter 29. How to design a parking lot using object-oriented principles?
Source :
www.andiamogo.com/S-OOD.pdf
More References :
https://www.quora.com/How-do-I-answer-design-related-questions-like-design-a-parking-lot-in-an-Amazon-intervie
http://stackoverflow.com/questions/764933/amazon-interview-question-design-an-oo-parking-lot
Source
https://www.geeksforgeeks.org/design-parking-lot-using-object-oriented-principles/
166
Chapter 30
How to prevent Singleton Pattern from Reflection, Serialization and Cloning? - Geeks-
forGeeks
Prerequisite: Singleton Pattern
In this article, we will see that what are various concepts which can break singleton property
of a class and how to avoid them. There are mainly 3 concepts which can break singleton
property of a class. Let’s discuss them one by one.
167
Chapter 30. How to prevent Singleton Pattern from Reflection, Serialization and Cloning?
}
public class GFG
{
public static void main(String[] args)
{
Singleton instance1 = Singleton.instance;
Singleton instance2 = null;
try
{
Constructor[] constructors =
Singleton.class.getDeclaredConstructors();
for (Constructor constructor : constructors)
{
// Below code will destroy the singleton pattern
constructor.setAccessible(true);
instance2 = (Singleton) constructor.newInstance();
break;
}
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("instance1.hashCode():- "
+ instance1.hashCode());
System.out.println("instance2.hashCode():- "
+ instance2.hashCode());
}
}
Output:-
instance1.hashCode():- 366712642
instance2.hashCode():- 1829164700
After running this class, you will see that hashCodes are different that means, 2 objects
of same class are created and singleton pattern has been destroyed.
Overcome reflection issue: To overcome issue raised by reflection, enums are used
because java ensures internally that enum value is instantiated only once. Since java
Enums are globally accessible, they can be used for singletons. Its only drawback is
that it is not flexible i.e it does not allow lazy initialization.
168
Chapter 30. How to prevent Singleton Pattern from Reflection, Serialization and Cloning?
{
INSTANCE;
}
As enums don’t have any constructor so it is not possible for Reflection to utilize
it. Enums have their by-default constructor, we can’t invoke them by ourself. JVM
handles the creation and invocation of enum constructors internally. As
enums don’t give their constructor definition to the program, it is not possible for us
to access them by Reflection also. Hence, reflection can’t break singleton property in
case of enums.
2. Serialization:- Serialization can also cause breakage of singleton property of singleton
classes. Serialization is used to convert an object of byte stream and save in a file or
send over a network. Suppose you serialize an object of a singleton class. Then if you
de-serialize that object it will create a new instance and hence break the singleton
pattern.
169
Chapter 30. How to prevent Singleton Pattern from Reflection, Serialization and Cloning?
Output:-
instance1 hashCode:- 1550089733
instance2 hashCode:- 865113938
As you can see, hashCode of both instances is different, hence there are 2 objects of a
singleton class. Thus, the class is no more singleton.
Overcome serialization issue:- To overcome this issue, we have to implement
method readResolve() method.
170
Chapter 30. How to prevent Singleton Pattern from Reflection, Serialization and Cloning?
private Singleton()
{
// private constructor
}
// implement readResolve method
protected Object readResolve()
{
return instance;
}
}
public class GFG
{
public static void main(String[] args)
{
try
{
Singleton instance1 = Singleton.instance;
ObjectOutput out
= new ObjectOutputStream(new FileOutputStream("file.text"));
out.writeObject(instance1);
out.close();
// deserailize from file to object
ObjectInput in
= new ObjectInputStream(new FileInputStream("file.text"));
Singleton instance2 = (Singleton) in.readObject();
in.close();
System.out.println("instance1 hashCode:- "
+ instance1.hashCode());
System.out.println("instance2 hashCode:- "
+ instance2.hashCode());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Output:-
instance1 hashCode:- 1550089733
instance2 hashCode:- 1550089733
171
Chapter 30. How to prevent Singleton Pattern from Reflection, Serialization and Cloning?
Output :-
instance1 hashCode:- 366712642
instance2 hashCode:- 1829164700
172
Chapter 30. How to prevent Singleton Pattern from Reflection, Serialization and Cloning?
Two different hashCode means there are 2 different objects of singleton class.
Overcome Cloning issue:- To overcome this issue, override clone() method and
throw an exception from clone method that is CloneNotSupportedException. Now
whenever user will try to create clone of singleton object, it will throw exception and
hence our class remains singleton.
173
Chapter 30. How to prevent Singleton Pattern from Reflection, Serialization and Cloning?
Output:-
Exception in thread "main" java.lang.CloneNotSupportedException
at GFG.Singleton.clone(GFG.java:29)
at GFG.GFG.main(GFG.java:38)
Now we have stopped user to create clone of singleton class. If you don;t want to
throw exception you can also return the same instance from clone method.
174
Chapter 30. How to prevent Singleton Pattern from Reflection, Serialization and Cloning?
Output:-
instance1 hashCode:- 366712642
instance2 hashCode:- 366712642
Now, as hashcode of both the instances is same that means they represent a single
instance.
Source
https://www.geeksforgeeks.org/prevent-singleton-pattern-reflection-serialization-cloning/
175
Chapter 31
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// creating a list
vector<int> list;
// elements to be added at the end.
// in the above created list.
list.push_back(1);
list.push_back(2);
list.push_back(3);
176
Chapter 31. Implementing Iterator pattern of a single Linked List
Output
1 2 3
One of the beauty of cin and cout is that they don’t demand format specifiers to work with
the type of data. This combined with templates make the code much cleaner and readable.
Although I prefer naming method in C++ start with caps, this implementation follows STL
rules to mimic exact set of method calls, viz push_back, begin, end.
Here is our own implementation of LinkedList and its Iterator pattern:
177
Chapter 31. Implementing Iterator pattern of a single Linked List
{
return Iterator(m_spRoot);
}
// End of LInkedList wrapped in Iterator type
Iterator end()
{
return Iterator(nullptr);
}
// Adds data to the end of list
void push_back(T data);
void Traverse();
// Iterator class can be used to
// sequentially access nodes of linked list
class Iterator
{
public:
Iterator() noexcept :
m_pCurrentNode (m_spRoot) { }
Iterator(const Node* pNode) noexcept :
m_pCurrentNode (pNode) { }
Iterator& operator=(Node* pNode)
{
this->m_pCurrentNode = pNode;
return *this;
}
// Prefix ++ overload
Iterator& operator++()
{
if (m_pCurrentNode)
m_pCurrentNode = m_pCurrentNode->pNext;
return *this;
}
// Postfix ++ overload
Iterator operator++(int)
{
Iterator iterator = *this;
++*this;
return iterator;
}
178
Chapter 31. Implementing Iterator pattern of a single Linked List
179
Chapter 31. Implementing Iterator pattern of a single Linked List
template <typename T>
void LinkedList<T>::push_back(T data)
{
Node* pTemp = GetNode(data);
if (!GetRootNode())
{
GetRootNode() = pTemp;
}
else
{
Node* pCrawler = GetRootNode();
while (pCrawler->pNext)
{
pCrawler = pCrawler->pNext;
}
pCrawler->pNext = pTemp;
}
}
template <typename T>
void LinkedList<T>::Traverse()
{
Node* pCrawler = GetRootNode();
while (pCrawler)
{
cout << pCrawler->data << " ";
pCrawler = pCrawler->pNext;
}
cout << endl;
}
//Driver program
int main()
{
LinkedList<int> list;
// Add few items to the end of LinkedList
list.push_back(1);
list.push_back(2);
list.push_back(3);
cout << "Traversing LinkedList through method" << endl;
list.Traverse();
180
Chapter 31. Implementing Iterator pattern of a single Linked List
Output:
Exercise:
The above implementation works well when we have one data. Extend this code to work
for set of data wrapped in a class.
Source
https://www.geeksforgeeks.org/implementing-iterator-pattern-of-a-single-linked-list/
181
Chapter 32
182
Chapter 32. Intercepting Filter Pattern
Design components
• Filter Manager : The FilterManager manages filter processing. It creates the Fil-
terChain with the appropriate filters, in the correct order, and initiates processing.
• FilterChain : The FilterChain is an ordered collection of independent filters.
• FilterOne, FilterTwo : These are the individual filters that are mapped to a target.
The FilterChain coordinates their processing.
• Target : The Target is the resource requested by the client.
183
Chapter 32. Intercepting Filter Pattern
184
Chapter 32. Intercepting Filter Pattern
{
this.filterManager = filterManager;
}
public void sendRequest(String request)
{
filterManager.filterRequest(request);
}
}
class InterceptingFilter
{
public static void main(String[] args)
{
FilterManager filterManager = new FilterManager(new Target());
filterManager.setFilter(new AuthenticationFilter());
filterManager.setFilter(new DebugFilter());
Client client = new Client();
client.setFilterManager(filterManager);
client.sendRequest("Downloads");
}
}
Output:
Authenticating : Downloads
Log: Downloads
Executing : Downloads
Advantages :
Disadvantages :
Source
https://www.geeksforgeeks.org/intercepting-filter-pattern/
185
Chapter 33
For Example :
Here is the hierarchy of expressions for “+ – 9 8 7” :
186
Chapter 33. Interpreter Design Pattern
Design components
187
Chapter 33. Interpreter Design Pattern
188
Chapter 33. Interpreter Design Pattern
189
Chapter 33. Interpreter Design Pattern
Output:
true
true
false
true
false
In the above code , We are creating an interface Expression and concrete classes imple-
menting the Expression interface. A class TerminalExpression is defined which acts as
a main interpreter and other classes OrExpression, AndExpression are used to create
combinational expressions.
Advantages
• It’s easy to change and extend the grammar. Because the pattern uses classes to
represent grammar rules, you can use inheritance to change or extend the grammar.
Existing expressions can be modified incrementally, and new expressions can be defined
as variations on old ones.
• Implementing the grammar is easy, too. Classes defining nodes in the abstract syntax
tree have similar implementations. These classes are easy to write, and often their
generation can be automated with a compiler or parser generator.
Disadvantages
• Complex grammars are hard to maintain. The Interpreter pattern defines at least one
class for every rule in the grammar. Hence grammars containing many rules can be
hard to manage and maintain.
Source
https://www.geeksforgeeks.org/interpreter-design-pattern/
190
Chapter 34
Iterator Pattern
And if it were some other collection like set, tree etc. way of iterating would change slightly.
Now, what if we build an iterator that provides a generic way of iterating over a collection
independent of its type.
// Create an iterator
Iterator iterator = notificationList.createIterator();
191
Chapter 34. Iterator Pattern
// anything else.
while (iterator.hasNext())
{
Notification notification = iterator.next());
}
Here we have a common interface Aggregate for client as it decouples it from the imple-
mentation of your collection of objects. The ConcreteAggregate implements createIterator()
that returns iterator for its collection. Each ConcreteAggregate’s responsibility is to instan-
tiate a ConcreteIterator that can iterate over its collection of objects. The iterator interface
provides a set of methods for traversing or modifying the collection that is in addition to
next()/hasNext() it can also provide functions for search, remove etc.
Let’s understand this through an example. Suppose we are creating a notification bar in
our application that displays all the notifications which are held in a notification collection.
NotificationCollection provides an iterator to iterate over its elements without exposing how
it has implemented the collection (array in this case) to the Client (NotificationBar).
192
Chapter 34. Iterator Pattern
193
Chapter 34. Iterator Pattern
194
Chapter 34. Iterator Pattern
Notification[] notificationList;
// maintains curr pos of iterator over the array
int pos = 0;
// Constructor takes the array of notifiactionList are
// going to iterate over.
public NotificationIterator (Notification[] notificationList)
{
this.notificationList = notificationList;
}
public Object next()
{
// return next element in the array and increment pos
Notification notification = notificationList[pos];
pos += 1;
return notification;
}
public boolean hasNext()
{
if (pos >= notificationList.length ||
notificationList[pos] == null)
return false;
else
return true;
}
}
// Contains collection of notifications as an object of
// NotificationCollection
class NotificationBar
{
NotificationCollection notifications;
public NotificationBar(NotificationCollection notifications)
{
this.notifications = notifications;
}
public void printNotifications()
{
Iterator iterator = notifications.createIterator();
System.out.println("-------NOTIFICATION BAR------------");
while (iterator.hasNext())
{
Notification n = (Notification)iterator.next();
195
Chapter 34. Iterator Pattern
System.out.println(n.getNotification());
}
}
}
// Driver class
class Main
{
public static void main(String args[])
{
NotificationCollection nc = new NotificationCollection();
NotificationBar nb = new NotificationBar(nc);
nb.printNotifications();
}
}
Output:
-------NOTIFICATION BAR------------
Notification 1
Notification 2
Notification 3
Notice that if we would have used ArrayList instead of Array there will not be any change
in the client (notification bar) code due to the decoupling achieved by the use of iterator
interface.
References:
Head First Design Patterns
Improved By : Shraddhesh Bhandari
Source
https://www.geeksforgeeks.org/iterator-pattern/
196
Chapter 35
Pros:
197
Chapter 35. Java Singleton Design Pattern Practices with Examples
Cons:
(a) May lead to resource wastage. Because instance of class is created always,
whether it is required or not.
(b) CPU time is also wasted in creation of instance if it is not required.
(c) Exception handling is not possible.
2. Using static block: This is also a sub part of Eager initialization. The only
difference is object is created in a static block so that we can have access on its
creation, like exception handling. In this way also, object is created at the time of
class loading.
It can be used when there is a chance of exceptions in creating object with eager
initialization.
Pros:
Cons:
(a) May lead to resource wastage. Because instance of class is created always,
whether it is required or not.
(b) CPU time is also wasted in creation of instance if it is not required.
198
Chapter 35. Java Singleton Design Pattern Practices with Examples
3. Lazy initialization: In this method, object is created only if it is needed. This may
prevent resource wastage. An implementation of getInstance() method is required
which return the instance. There is a null check that if object is not created then create,
otherwise return previously created. To make sure that class cannot be instantiated
in any other way, constructor is made final. As object is created with in a method, it
ensures that object will not be created until and unless it is required. Instance is kept
private so that no one can access it directly.
It can be used in a single threaded environment because multiple threads can break
singleton property because they can access get instance method simultaneously and
create multiple objects.
Pros:
(a) Object is created only if it is needed. It may overcome resource overcome and
wastage of CPU time.
(b) Exception handling is also possible in method.
Cons:
199
Chapter 35. Java Singleton Design Pattern Practices with Examples
4. Thread Safe Singleton: A thread safe singleton in created so that singleton property
is maintained even in multithreaded environment. To make a singleton class thread-
safe, getInstance() method is made synchronized so that multiple threads can’t access
it simultaneously.
Pros:
Cons:
200
Chapter 35. Java Singleton Design Pattern Practices with Examples
Pros:
Cons:
As cons. of double check locking method is bearable so it can be used for high perfor-
mance multi-threaded applications.
6. Bill Pugh Singleton Implementation: Prior to Java5, memory model had a lot
of issues and above methods caused failure in certain scenarios in multithreaded envi-
ronment. So, Bill Pugh suggested a concept of inner static classes to use for singleton.
201
Chapter 35. Java Singleton Design Pattern Practices with Examples
{
// private constructor
}
// Inner class to provide instance of class
private static class BillPughSingleton
{
private static final GFG INSTANCE = new GFG();
}
public static GFG getInstance()
{
return BillPughSingleton.INSTANCE;
}
}
When the singleton class is loaded, inner class is not loaded and hence doesn’t create
object when loading the class. Inner class is created only when getInstance() method
is called. So it may seem like eager initialization but it is lazy initialization.
This is the most widely used approach as it doesn’t use synchronization.
1. Eager initialization is easy to implement but it may cause resource and CPU time
wastage. Use it only if cost of initializing a class is less in terms of resources or your
program will always need the instance of class.
2. By using Static block in Eager initialization we can provide exception handling and
also can control over instance.
3. Using synchronized we can create singleton class in multi-threading environment also
but it can cause slow performance, so we can use Double check locking mechanism.
4. Bill Pugh implementation is most widely used approach for singleton classes. Most
developers prefer it because of its simplicity and advantages.
Source
https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/
202
Chapter 36
• Lazy loading is just a fancy name given to the process of initializing a class when it’s
actually needed.
• In simple words, Lazy loading is a software design pattern where the initialization of
an object occurs only when it is actually needed and not before to preserve simplicity
of usage and improve performance.
• Lazy loading is essential when the cost of object creation is very high and the use
of the object is very rare. So this is the scenario where it’s worth implementing lazy
loading.The fundamental idea of lazy loading is to load object/data when needed.
For Example, Suppose You are creating an application in which there is a Company object
and this object contains a list of employees of the company in a ContactList object. There
could be thousands of employees in a company. Loading the Company object from the
database along with the list of all its employees in the ContactList object could be very
time consuming. In some cases you don’t even require the list of the employees, but you
are forced to wait until the company and its list of employees loaded into the memory.
One way to save time and memory is to avoid loading of the employee objects until required
and this is done using the Lazy Loading Design Pattern.
203
Chapter 36. Lazy Loading Design Pattern
1. Virtual proxy
2. Lazy initialization
3. Ghost
4. Value holder
Virtual proxy
The Virtual Proxy pattern is a memory saving technique that recommends postponing an
object creation until it is needed. It is used when creating an object the is expensive in
terms of memory usage or processing involved.
204
Chapter 36. Lazy Loading Design Pattern
205
Chapter 36. Lazy Loading Design Pattern
206
Chapter 36. Lazy Loading Design Pattern
Output:
Now, In the above code have a Company object is created with a proxy ContactList object.
At this time, the Company object holds a proxy reference, not the real ContactList object’s
reference, so there no employee list loaded into the memory.
Lazy Initialization
The Lazy Initialization technique consists of checking the value of a class field when it’s
being used. If that value equals to null then that field gets loaded with the proper value
before it is returned.
Here is the example :
207
Chapter 36. Lazy Loading Design Pattern
none,
Audi,
BMW,
}
class Car {
private static Map<CarType, Car> types = new HashMap<>();
private Car(CarType type) {}
public static Car getCarByTypeName(CarType type)
{
Car Car;
if (!types.containsKey(type)) {
// Lazy initialisation
Car = new Car(type);
types.put(type, Car);
} else {
// It's available currently
Car = types.get(type);
}
return Car;
}
public static Car getCarByTypeNameHighConcurrentVersion(CarType type)
{
if (!types.containsKey(type)) {
synchronized(types)
{
// Check again, after having acquired the lock to make sure
// the instance was not created meanwhile by another thread
if (!types.containsKey(type)) {
// Lazy initialisation
types.put(type, new Car(type));
}
}
}
return types.get(type);
}
public static void showAll()
{
if (types.size() > 0) {
System.out.println("Number of instances made = " + types.size());
208
Chapter 36. Lazy Loading Design Pattern
for (Entry<CarType, Car> entry : types.entrySet()) {
String Car = entry.getKey().toString();
Car = Character.toUpperCase(Car.charAt(0)) + Car.substring(1);
System.out.println(Car);
}
System.out.println();
}
}
}
class Program {
public static void main(String[] args)
{
Car.getCarByTypeName(CarType.BMW);
Car.showAll();
Car.getCarByTypeName(CarType.Audi);
Car.showAll();
Car.getCarByTypeName(CarType.BMW);
Car.showAll();
}
}
Output :
Value Holder
Basically, A value holder is a generic object that handles the lazy loading behavior and
appears in place of the object’s data fields.When the user needs to access it, they simply
ask the value holder for its value by calling the GetValue method. At that time (and only
then), the value gets loaded from a database or from a service.(this is not always needed).
209
Chapter 36. Lazy Loading Design Pattern
Note : The main drawback of this approach is that the user has to know that a value holder
is expected.
Ghost
A ghost is the object that is to be loaded in a partial state. It corresponds to the real object
but not in its full state. It may be empty or it may contain just some fields (such as the
ID). When the user tries to access some fields that haven’t been loaded yet, the ghost object
fully initializes itself (this is not always needed).
For example, Let’s consider that a developer what add an online form so that any user can
request content via that online form. At the time of creation all we know is that content
will be accessed but what action or content is unknown to the user.
$userData = array(
"UID" = > uniqid(),
"requestTime" = > microtime(true),
"dataType" = > "",
"request" = > "");
if (isset($_POST['data']) && $userData) {
//...
}
In the above PHP example, the content from the online form can be accessed to the user in
the form of text file or any source.
210
Chapter 36. Lazy Loading Design Pattern
• request is the boolean function to notify the user about the request being completed
or not.
Advantages
• This approach is a faster application start-up time as it is not required to created and
load all of the application objects.
Disadvantages
• The code becomes complicated as we need to check if loading is needed or not. So this
may cause slower in the performance.
Source
https://www.geeksforgeeks.org/lazy-loading-design-pattern/
211
Chapter 37
212
Chapter 37. MVC Design Pattern
Design components
• The Model contains only the pure application data, it contains no logic describing
how to present the data to a user.
• The View presents the model’s data to the user. The view knows how to access the
model’s data, but it does not know what this data means or what the user can do to
manipulate it.
• The Controller exists between the view and the model. It listens to events triggered
by the view (or another external source) and executes the appropriate reaction to
these events. In most cases, the reaction is to call a method on the model. Since the
view and the model are connected through a notification mechanism, the result of this
action is then automatically reflected in the view.
class Student
{
private String rollNo;
private String name;
213
Chapter 37. MVC Design Pattern
public String getRollNo()
{
return rollNo;
}
public void setRollNo(String rollNo)
{
this.rollNo = rollNo;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
class StudentView
{
public void printStudentDetails(String studentName, String studentRollNo)
{
System.out.println("Student: ");
System.out.println("Name: " + studentName);
System.out.println("Roll No: " + studentRollNo);
}
}
class StudentController
{
private Student model;
private StudentView view;
public StudentController(Student model, StudentView view)
{
this.model = model;
this.view = view;
}
public void setStudentName(String name)
{
model.setName(name);
}
214
Chapter 37. MVC Design Pattern
215
Chapter 37. MVC Design Pattern
Output:
Student:
Name: Lokesh Sharma
Roll No: 15UCS157
Student:
Name: Vikram Sharma
Roll No: 15UCS157
Advantages
• Multiple developers can work simultaneously on the model, controller and views.
• MVC enables logical grouping of related actions on a controller together. The views
for a specific model are also grouped together.
• Models can have multiple views.
Disadvantages
• The framework navigation can be complex because it introduces new layers of abstrac-
tion and requires users to adapt to the decomposition criteria of MVC.
• Knowledge on multiple technologies becomes the norm. Developers using MVC need
to be skilled in multiple technologies.
Source
https://www.geeksforgeeks.org/mvc-design-pattern/
216
Chapter 38
217
Chapter 38. Mediator Design Pattern
218
Chapter 38. Mediator Design Pattern
@Override
public void findHighestBidder()
{
int maxBid = 0;
Buyer winner = null;
for (Buyer b : buyers) {
if (b.price > maxBid) {
maxBid = b.price;
winner = b;
}
}
System.out.println("The auction winner is " + winner.name +
". He paid " + winner.price + "$ for the item.");
}
}
public abstract class Buyer {
// this class holds the buyer
protected Mediator mediator;
protected String name;
protected int price;
public Buyer(Mediator med, String name)
{
this.mediator = med;
this.name = name;
}
public abstract void bid(int price);
public abstract void cancelTheBid();
}
public class AuctionBuyer extends Buyer {
// implementation of the bidding proccess
// There is an option to bid and an option to
// cancel the bidding
public AuctionBuyer(Mediator mediator,
String name)
{
super(mediator, name);
}
@Override
public void bid(int price)
219
Chapter 38. Mediator Design Pattern
{
this.price = price;
}
@Override
public void cancelTheBid()
{
this.price = -1;
}
}
public class Main {
/* This program illustrate an auction. The AuctionMediator
is responsible for adding the buyers, and after each
buyer bid a certain amount for the item, the mediator
know who won the auction. */
public static void main(String[] args)
{
AuctionMediator med = new AuctionMediator();
Buyer b1 = new AuctionBuyer(med, "Tal Baum");
Buyer b2 = new AuctionBuyer(med, "Elad Shamailov");
Buyer b3 = new AuctionBuyer(med, "John Smith");
// Crate and add buyers
med.addBuyer(b1);
med.addBuyer(b2);
med.addBuyer(b3);
System.out.println("Welcome to the auction. Tonight " +
"we are selling a vacation to Vegas." +
" please Bid your offers.");
System.out.println("--------------------------------" +
"---------------");
System.out.println("Waiting for the buyer's offers...");
// Making bids
b1.bid(1800);
b2.bid(2000);
b3.bid(780);
System.out.println("---------------------------------" +
"--------------");
med.findHighestBidder();
b2.cancelTheBid();
System.out.print(b2.name + " Has canceled his bid!, " +
"in that case ");
220
Chapter 38. Mediator Design Pattern
med.findHighestBidder();
}
}
Output:
Advantages:
• Simplicity
• You can replace one object in the structure with a different one without affecting the
classes and the interfaces.
Disadvantages:
• The Mediator often needs to be very intimate with all the different classes, And it
makes it really complex.
• Can make it difficult to maintain.
Author : http://designpattern.co.il/
Source
https://www.geeksforgeeks.org/mediator-design-pattern-2/
221
Chapter 39
Design components
• Mediator :It defines the interface for communication between colleague objects.
• ConcreteMediator : It implements the mediator interface and coordinates commu-
nication between colleague objects.
222
Chapter 39. Mediator design pattern
223
Chapter 39. Mediator design pattern
224
Chapter 39. Mediator design pattern
class MediatorDesignPattern
{
public static void main(String args[])
{
IATCMediator atcMediator = new ATCMediator();
Flight sparrow101 = new Flight(atcMediator);
Runway mainRunway = new Runway(atcMediator);
atcMediator.registerFlight(sparrow101);
atcMediator.registerRunway(mainRunway);
sparrow101.getReady();
mainRunway.land();
sparrow101.land();
}
}
Output:
Advantage
Disadvantage
• It centralizes control. The mediator pattern trades complexity of interaction for com-
plexity in the mediator. Because a mediator encapsulates protocols, it can become
more complex than any individual colleague. This can make the mediator itself a
monolith that’s hard to maintain
Source
https://www.geeksforgeeks.org/mediator-design-pattern/
225
Chapter 40
Design components
• originator : the object for which the state is to be saved. It creates the memento
and uses it in future to undo.
• memento : the object that is going to maintain the state of originator. Its just a
POJO.
• caretaker : the object that keeps track of multiple memento. Like maintaining
savepoints.
A Caretaker would like to perform an operation on the Originator while having the
possibility to rollback. The caretaker calls the createMemento() method on the originator
asking the originator to pass it a memento object. At this point the originator creates a
memento object saving its internal state and passes the memento to the caretaker. The
caretaker maintains the memento object and performs the operation. In case of the need
to undo the operation, the caretaker calls the setMemento() method on the originator
passing the maintained memento object. The originator would accept the memento, using
it to restore its previous state.
Let’s see an example of Memento design pattern.
226
Chapter 40. Memento design pattern
import java.util.List;
import java.util.ArrayList;
class Life
{
private String time;
public void set(String time)
{
System.out.println("Setting time to " + time);
this.time = time;
}
public Memento saveToMemento()
{
System.out.println("Saving time to Memento");
return new Memento(time);
}
public void restoreFromMemento(Memento memento)
{
time = memento.getSavedTime();
System.out.println("Time restored from Memento: " + time);
}
public static class Memento
{
private final String time;
public Memento(String timeToSave)
{
time = timeToSave;
}
public String getSavedTime()
{
return time;
}
}
}
class Design
{
public static void main(String[] args)
{
List<Life.Memento> savedTimes = new ArrayList<Life.Memento>();
227
Chapter 40. Memento design pattern
Life life = new Life();
//time travel and record the eras
life.set("1000 B.C.");
savedTimes.add(life.saveToMemento());
life.set("1000 A.D.");
savedTimes.add(life.saveToMemento());
life.set("2000 A.D.");
savedTimes.add(life.saveToMemento());
life.set("4000 A.D.");
life.restoreFromMemento(savedTimes.get(0));
}
}
Output:
Advantage
Disadvantage
• If Originator object is very huge then Memento object size will also be huge and use
a lot of memory.
Source
https://www.geeksforgeeks.org/memento-design-pattern/
228
Chapter 41
Design components
• Client : This class has a dependency that may or may not be required. Where no
functionality is required in the dependency, it will execute the methods of a null object.
• DependencyBase : This abstract class is the base class for the various available
dependencies that the Client may use. This is also the base class for the null object
class. Where the base class provides no shared functionality, it may be replaced with
an interface.
• Dependency : This class is a functional dependency that may be used by the Client.
• NullObject : This is the null object class that can be used as a dependency by the
Client. It contains no functionality but implements all of the members defined by the
DependencyBase abstract class.
229
Chapter 41. Null object Design Pattern
230
Chapter 41. Null object Design Pattern
Output:
Lokesh
Kushagra
Vikram
Not Available
Advantages :
• It defines class hierarchies consisting of real objects and null objects. Null objects can
be used in place of real objects when the object is expected to do nothing. Whenever
client code expects a real object, it can also take a null object.
• Also makes the client code simple. Clients can treat real collaborators and null collab-
orators uniformly. Clients normally don’t know whether they’re dealing with a real
or a null collaborator. This simplifies client code, because it avoids having to write
testing code which handles the null collaborator specially.
231
Chapter 41. Null object Design Pattern
Disadvantages :
• Can be difficult to implement if various clients do not agree on how the null object
should do nothing as when your AbstractObject interface is not well defined.
• Can necessitate creating a new NullObject class for every new AbstractObject class.
Source
https://www.geeksforgeeks.org/null-object-design-pattern/
232
Chapter 42
• Creation
• Validation
• Destroy.
233
Chapter 42. Object Pool Design Pattern
234
Chapter 42. Object Pool Design Pattern
• Client : This is the class that uses an object of the PooledObject type.
• ReuseablePool: The PooledObject class is the type that is expensive or slow to
instantiate, or that has limited availability, so is to be held in the object pool.
• ObjectPool : The Pool class is the most important class in the object pool design
pattern. ObjectPool maintains a list of available objects and a collection of objects
that have already been requested from the pool.
Let’s take the example of the database connections. It’s obviously that opening too many
connections might affect the performance for several reasons:
Here the object pool manages the connections and provide a way to reuse and share them.
It can also limit the maximum number of objects that can be created.
235
Chapter 42. Object Pool Design Pattern
unlock.remove(t);
dead(t);
t = null;
}
else {
if (validate(t)) {
unlock.remove(t);
lock.put(t, now);
return (t);
}
else {
// object failed validation
unlock.remove(t);
dead(t);
t = null;
}
}
}
}
// no objects available, create a new one
t = create();
lock.put(t, now);
return (t);
}
synchronized void takeIn(T t)
{
lock.remove(t);
unlock.put(t, System.currentTimeMillis());
}
}
// Three methods are abstract
// and therefore must be implemented by the subclass
class JDBCConnectionPool extends ObjectPool<Connection> {
String dsn, usr, pwd;
JDBCConnectionPool(String driver, String dsn, String usr, String pwd)
{
super();
try {
Class.forName(driver).newInstance();
}
catch (Exception e) {
e.printStackTrace();
}
this.dsn = dsn;
this.usr = usr;
236
Chapter 42. Object Pool Design Pattern
this.pwd = pwd;
}
Connection create()
{
try {
return (DriverManager.getConnection(dsn, usr, pwd));
}
catch (SQLException e) {
e.printStackTrace();
return (null);
}
}
void dead(Connection o)
{
try {
((Connection)o).close();
}
catch (SQLException e) {
e.printStackTrace();
}
}
boolean validate(Connection o)
{
try {
return (!((Connection)o).isClosed());
}
catch (SQLException e) {
e.printStackTrace();
return (false);
}
}
}
class Main {
public static void main(String args[])
{
// Create the ConnectionPool:
JDBCConnectionPool pool = new JDBCConnectionPool(
"org.hsqldb.jdbcDriver", "jdbc:hsqldb: //localhost/mydb",
"sa", "password");
// Get a connection:
Connection con = pool.takeOut();
// Return the connection:
pool.takeIn(con);
237
Chapter 42. Object Pool Design Pattern
}
}
Advantages
Reference :
https://sourcemaking.com/design_patterns/object_pool
Source
https://www.geeksforgeeks.org/object-pool-design-pattern/
238
Chapter 43
239
Chapter 43. Observer Pattern | Set 1 (Introduction)
cordingly.
Below is the java implementation of this design.
240
Chapter 43. Observer Pattern | Set 1 (Introduction)
241
Chapter 43. Observer Pattern | Set 1 (Introduction)
{
System.out.println("\nAverage Score Display:\n" +
"Run Rate: " + runRate +
"\nPredictedScore: " + predictedScore);
}
}
// A class to display score. Data of this class is
// updated by CricketData
class CurrentScoreDisplay
{
private int runs, wickets;
private float overs;
public void update(int runs,int wickets,float overs)
{
this.runs = runs;
this.wickets = wickets;
this.overs = overs;
display();
}
public void display()
{
System.out.println("\nCurrent Score Display: \n" +
"Runs: " + runs +"\nWickets:"
+ wickets + "\nOvers: " + overs );
}
}
// Driver class
class Main
{
public static void main(String args[])
{
// Create objects for testing
AverageScoreDisplay averageScoreDisplay =
new AverageScoreDisplay();
CurrentScoreDisplay currentScoreDisplay =
new CurrentScoreDisplay();
// Pass the displays to Cricket data
CricketData cricketData = new CricketData(currentScoreDisplay,
averageScoreDisplay);
// In real app you would have some logic to call this
// function when data changes
cricketData.dataChanged();
242
Chapter 43. Observer Pattern | Set 1 (Introduction)
}
}
Output:
• CricketData holds references to concrete display element objects even though it needs
to call only the update method of these objects. It has access to too much additional
information it than it requires.
• This statement “currentScoreDisplay.update(runs,wickets,overs);” violates one of the
most important design principle “Program to interfaces, not implementations.” as we
are using concrete objects to share data rather than abstract interfaces.
• CricketData and display elements are tightly coupled.
• If in future another requirement comes in and we need another display element to
be added we need to make changes to the non-varying part of our code(CricketData).
This is definitely not a good design practice and application might not be able to
handle changes and not easy to maintain.
Definition:
243
Chapter 43. Observer Pattern | Set 1 (Introduction)
The Observer Pattern defines a one to many dependency between objects so that one object
changes state, all of its dependents are notified and updated automatically.
Explanation:
Class diagram:
Image Source : Wikipedia
• Here Observer and Subject are interfaces(can be any abstract super type not neces-
sarily java interface).
• All observers who need the data need to implement observer interface.
• notify() method in observer interface defines the action to be taken when the subject
provides it data.
• The subject maintains an observerCollection which is simply the list of currently reg-
istered(subscribed) observers.
• registerObserver(observer) and unregisterObserver(observer) are methods to add and
remove observers respectively.
• notifyObservers() is called when the data is changed and the observers need to be
supplied with new data.
Advantages:
Provides a loosely coupled design between objects that interact. Loosely coupled objects are
flexible with changing requirements. Here loose coupling means that the interacting objects
should have less information about each other.
Observer pattern provides this loose coupling as:
244
Chapter 43. Observer Pattern | Set 1 (Introduction)
Disadvantages:
• Memory leaks caused by Lapsed listener problem because of explicit register and un-
registering of observers.
• It is heavily used in GUI toolkits and event listener. In java the button(subject) and
onClickListener(observer) are modelled with observer pattern.
• Social media, RSS feeds, email subscription in which you have the option to follow or
subscribe and you receive latest notification.
• All users of an app on play store gets notified if there is an update.
Source
https://www.geeksforgeeks.org/observer-pattern-set-1-introduction/
245
Chapter 44
246
Chapter 44. Observer Pattern | Set 2 (Implementation)
Java Implementation:
247
Chapter 44. Observer Pattern | Set 2 (Implementation)
}
@Override
public void unregisterObserver(Observer o) {
observerList.remove(observerList.indexOf(o));
}
@Override
public void notifyObservers()
{
for (Iterator<Observer> it =
observerList.iterator(); it.hasNext();)
{
Observer o = it.next();
o.update(runs,wickets,overs);
}
}
// get latest runs from stadium
private int getLatestRuns()
{
// return 90 for simplicity
return 90;
}
// get latest wickets from stadium
private int getLatestWickets()
{
// return 2 for simplicity
return 2;
}
// get latest overs from stadium
private float getLatestOvers()
{
// return 90 for simplicity
return (float)10.2;
}
// This method is used update displays
// when data changes
public void dataChanged()
{
//get latest data
runs = getLatestRuns();
wickets = getLatestWickets();
overs = getLatestOvers();
248
Chapter 44. Observer Pattern | Set 2 (Implementation)
notifyObservers();
}
}
// This interface is implemented by all those
// classes that are to be updated whenever there
// is an update from CricketData
interface Observer
{
public void update(int runs, int wickets,
float overs);
}
class AverageScoreDisplay implements Observer
{
private float runRate;
private int predictedScore;
public void update(int runs, int wickets,
float overs)
{
this.runRate =(float)runs/overs;
this.predictedScore = (int)(this.runRate * 50);
display();
}
public void display()
{
System.out.println("\nAverage Score Display: \n"
+ "Run Rate: " + runRate +
"\nPredictedScore: " +
predictedScore);
}
}
class CurrentScoreDisplay implements Observer
{
private int runs, wickets;
private float overs;
public void update(int runs, int wickets,
float overs)
{
this.runs = runs;
this.wickets = wickets;
this.overs = overs;
display();
}
249
Chapter 44. Observer Pattern | Set 2 (Implementation)
public void display()
{
System.out.println("\nCurrent Score Display:\n"
+ "Runs: " + runs +
"\nWickets:" + wickets +
"\nOvers: " + overs );
}
}
// Driver Class
class Main
{
public static void main(String args[])
{
// create objects for testing
AverageScoreDisplay averageScoreDisplay =
new AverageScoreDisplay();
CurrentScoreDisplay currentScoreDisplay =
new CurrentScoreDisplay();
// pass the displays to Cricket data
CricketData cricketData = new CricketData();
// register display elements
cricketData.registerObserver(averageScoreDisplay);
cricketData.registerObserver(currentScoreDisplay);
// in real app you would have some logic to
// call this function when data changes
cricketData.dataChanged();
//remove an observer
cricketData.unregisterObserver(averageScoreDisplay);
// now only currentScoreDisplay gets the
// notification
cricketData.dataChanged();
}
}
Output:
250
Chapter 44. Observer Pattern | Set 2 (Implementation)
Runs: 90
Wickets:2
Overs: 10.2
Note: Now we can add/delete as many observers without changing the subject.
References:
1. https://en.wikipedia.org/wiki/Observer_pattern
2. Head First Design Patterns book (highly recommended)
Source
https://www.geeksforgeeks.org/observer-pattern-set-2-implementation/
251
Chapter 45
252
Chapter 45. Prototype Design Pattern
253
Chapter 45. Prototype Design Pattern
254
Chapter 45. Prototype Design Pattern
{
public static void main (String[] args)
{
ColorStore.getColor("blue").addColor();
ColorStore.getColor("black").addColor();
ColorStore.getColor("black").addColor();
ColorStore.getColor("blue").addColor();
}
}
Output :
255
Chapter 45. Prototype Design Pattern
• Overkill for a project that uses very few objects and/or does not have an underlying
emphasis on the extension of prototype chains.
• It also hides concrete product classes from the client
• Each subclass of Prototype must implement the clone() operation which may be dif-
ficult, when the classes under consideration already exist. Also implementing clone()
can be difficult when their internals include objects that don’t support copying or have
circular references.
Source
https://www.geeksforgeeks.org/prototype-design-pattern/
256
Chapter 46
257
Chapter 46. Proxy Design Pattern
258
Chapter 46. Proxy Design Pattern
A smart proxy provides additional layer of security by interposing specific actions when
the object is accessed. An example can be to check if the real object is locked before it is
accessed to ensure that no other object can change it.
Some Examples
A very simple real life scenario is our college internet, which restricts few site access. The
proxy first checks the host you are connecting to, if it is not part of restricted site list, then
it connects to the real internet. This example is based on Protection proxies.
Lets see how it works :
Interface of Internet
package com.saket.demo.proxy;
public interface Internet
{
public void connectTo(String serverhost) throws Exception;
}
RealInternet.java
package com.saket.demo.proxy;
public class RealInternet implements Internet
{
@Override
public void connectTo(String serverhost)
{
System.out.println("Connecting to "+ serverhost);
}
}
ProxyInternet.java
package com.saket.demo.proxy;
import java.util.ArrayList;
import java.util.List;
public class ProxyInternet implements Internet
{
private Internet internet = new RealInternet();
private static List<String> bannedSites;
static
{
259
Chapter 46. Proxy Design Pattern
Client.java
package com.saket.demo.proxy;
public class Client
{
public static void main (String[] args)
{
Internet internet = new ProxyInternet();
try
{
internet.connectTo("geeksforgeeks.org");
internet.connectTo("abc.com");
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
}
Connecting to geeksforgeeks.org
Access Denied
260
Chapter 46. Proxy Design Pattern
Benefits:
Drawbacks/Consequences:
This pattern introduces another layer of abstraction which sometimes may be an issue if the
RealSubject code is accessed by some of the clients directly and some of them might access
the Proxy classes. This might cause disparate behaviour.
Interesting points:
• There are few differences between the related patterns. Like Adapter pattern gives
a different interface to its subject, while Proxy patterns provides the same interface
from the original object but the decorator provides an enhanced interface. Decorator
pattern adds additional behaviour at runtime.
• Proxy used in Java API: java.rmi.*;
Source
https://www.geeksforgeeks.org/proxy-design-pattern/
261
Chapter 47
Design components
• Service Locator : The Service Locator abstracts the API lookup services, vendor
dependencies, lookup complexities, and business object creation, and provides a simple
262
Chapter 47. Service Locator Pattern
interface to clients. This reduces the client’s complexity. In addition, the same client
or other clients can reuse the Service Locator.
• InitialContext : The InitialContext object is the start point in the lookup and
creation process. Service providers provide the context object, which varies depending
on the type of business object provided by the Service Locator’s lookup and creation
service.
• ServiceFactory : The ServiceFactory object represents an object that provides life
cycle management for the BusinessService objects. The ServiceFactory object for
enterprise beans is an EJBHome object.
• BusinessService : The BusinessService is a role that is fulfilled by the service the
client is seeking to access. The BusinessService object is created or looked up or
removed by the ServiceFactory. The BusinessService object in the context of an EJB
application is an enterprise bean.
Suppose classes with dependencies on services whose concrete types are specified at compile
time.
In the above diagram, ClassA has compile time dependencies on ServiceA and ServiceB.But
this situation has drawbacks.
• If we want to replace or update the dependencies we must change the classes source
code and recompile the solution.
• The concrete implementation of the dependencies must be available at compile time.
263
Chapter 47. Service Locator Pattern
In simple words, Service Locator pattern does not describe how to instantiate the services.
It describes a way to register services and locate them.
Let’s see an example of Service Locator Pattern.
// Java program to
// illustrate Service Design Service
// Locator Pattern
import java.util.ArrayList;
import java.util.List;
// Service interface
// for getting name and
// Executing it.
interface Service {
public String getName();
public void execute();
}
// Service one implementing Locator
class ServiceOne implements Service {
public void execute()
{
System.out.println("Executing ServiceOne");
}
264
Chapter 47. Service Locator Pattern
@Override
public String getName()
{
return "ServiceOne";
}
}
// Service two implementing Locator
class ServiceTwo implements Service {
public void execute()
{
System.out.println("Executing ServiceTwo");
}
@Override
public String getName()
{
return "ServiceTwo";
}
}
// Checking the context
// for ServiceOne and ServiceTwo
class InitialContext {
public Object lookup(String name)
{
if (name.equalsIgnoreCase("ServiceOne")) {
System.out.println("Creating a new ServiceOne object");
return new ServiceOne();
}
else if (name.equalsIgnoreCase("ServiceTwo")) {
System.out.println("Creating a new ServiceTwo object");
return new ServiceTwo();
}
return null;
}
}
class Cache {
private List<Service> services;
public Cache()
{
services = new ArrayList<Service>();
}
public Service getService(String serviceName)
265
Chapter 47. Service Locator Pattern
{
for (Service service : services) {
if (service.getName().equalsIgnoreCase(serviceName)) {
System.out.println("Returning cached "
+ serviceName + " object");
return service;
}
}
return null;
}
public void addService(Service newService)
{
boolean exists = false;
for (Service service : services) {
if (service.getName().equalsIgnoreCase(newService.getName())) {
exists = true;
}
}
if (!exists) {
services.add(newService);
}
}
}
// Locator class
class ServiceLocator {
private static Cache cache;
static
{
cache = new Cache();
}
public static Service getService(String name)
{
Service service = cache.getService(name);
if (service != null) {
return service;
}
InitialContext context = new InitialContext();
Service ServiceOne = (Service)context.lookup(name);
cache.addService(ServiceOne);
return ServiceOne;
}
}
266
Chapter 47. Service Locator Pattern
// Driver class
class ServiceLocatorPatternDemo {
public static void main(String[] args)
{
Service service = ServiceLocator.getService("ServiceOne");
service.execute();
service = ServiceLocator.getService("ServiceTwo");
service.execute();
service = ServiceLocator.getService("ServiceOne");
service.execute();
service = ServiceLocator.getService("ServiceTwo");
service.execute();
}
}
Output:
Advantages :
Disadvantages :
• The registry makes the code more difficult to maintain (opposed to using Dependency
injection), because it becomes unclear when you would be introducing a breaking
change.
• The registry hides the class dependencies causing run-time errors instead of compile-
time errors when dependencies are missing.
267
Chapter 47. Service Locator Pattern
Strategies
The following strategies are used to implement service Locator Pattern :
EJB Service Locator Strategy : This strategy uses EJBHome object for enterprise
bean components and this EJBHome is cached in the ServiceLocator for future use when
the client needs the home object again.
JMS Queue Service Locator Strategy : This strategy is applicable to point to point
messaging requirements. The following the strategies under JMS Queue Service Locator
Strategy.
Type Checked Service Locator Strategy : This strategy has trade-offs. It reduces the
flexibility of lookup, which is in the Services Property Locator strategy, but add the type
checking of passing in a constant to the ServiceLocator.getHome() method.
Source
https://www.geeksforgeeks.org/service-locator-pattern/
268
Chapter 48
Normal class vs Singleton class: Difference in normal and singleton class in terms of
instantiation is that, For normal class we use constructor, whereas for singleton class we use
getInstance() method (Example code:I). In general, to avoid confusion we may also use the
class name as method name while defining this method (Example code:II).
Implementing Singleton class with getInstance() method
269
Chapter 48. Singleton Class in Java
private Singleton()
{
s = "Hello I am a string part of Singleton class";
}
// static method to create instance of Singleton class
public static Singleton getInstance()
{
if (single_instance == null)
single_instance = new Singleton();
return single_instance;
}
}
// Driver Class
class Main
{
public static void main(String args[])
{
// instantiating Singleton class with variable x
Singleton x = Singleton.getInstance();
// instantiating Singleton class with variable y
Singleton y = Singleton.getInstance();
// instantiating Singleton class with variable z
Singleton z = Singleton.getInstance();
// changing variable of instance x
x.s = (x.s).toUpperCase();
System.out.println("String from x is " + x.s);
System.out.println("String from y is " + y.s);
System.out.println("String from z is " + z.s);
System.out.println("\n");
// changing variable of instance z
z.s = (z.s).toLowerCase();
System.out.println("String from x is " + x.s);
System.out.println("String from y is " + y.s);
System.out.println("String from z is " + z.s);
}
}
Output:
270
Chapter 48. Singleton Class in Java
Explanation: In the Singleton class, when we first time call getInstance() method, it
creates an object of the class with name single_instance and return it to the variable. Since
single_instance is static, it is changed from null to some object. Next time, if we try to
call getInstance() method, since single_instance is not null, it is returned to the variable,
instead of instantiating the Singleton class again. This part is done by if condition.
In the main class, we instantiate the singleton class with 3 objects x, y, z by calling static
method getInstance(). But actually after creation of object x, variables y and z are pointed
to object x as shown in the diagram. Hence, if we change the variables of object x, that is
reflected when we access the variables of objects y and z. Also if we change the variables of
object z, that is reflected when we access the variables of objects x and y.
Implementing Singleton class with method name as that of class name
271
Chapter 48. Singleton Class in Java
}
// static method to create instance of Singleton class
public static Singleton Singleton()
{
// To ensure only one instance is created
if (single_instance == null)
{
single_instance = new Singleton();
}
return single_instance;
}
}
// Driver Code
class Main
{
public static void main(String args[])
{
// instantiating Singleton class with variable x
Singleton x = Singleton.Singleton();
// instantiating Singleton class with variable y
Singleton y = Singleton.Singleton();
// instantiating Singleton class with variable z
Singleton z = Singleton.Singleton();
// changing variable of instance x
x.s = (x.s).toUpperCase();
System.out.println("String from x is " + x.s);
System.out.println("String from y is " + y.s);
System.out.println("String from z is " + z.s);
System.out.println("\n");
// changing variable of instance x
z.s = (z.s).toLowerCase();
System.out.println("String from x is " + x.s);
System.out.println("String from y is " + y.s);
System.out.println("String from z is " + z.s);
}
}
Output:
272
Chapter 48. Singleton Class in Java
Explanation: In the Singleton class, when we first time call Singleton() method, it creates
an object of class Singleton with name single_instance and return it to the variable. Since
single_instance is static, it is changed from null to some object. Next time if we try to call
Singleton() method, since single_instance is not null, it is returned to the variable, instead
of instantiating the Singleton class again.
Source
https://www.geeksforgeeks.org/singleton-class-java/
273
Chapter 49
274
Chapter 49. Singleton Design Pattern | Implementation
{
if (obj==null)
obj = new Singleton();
return obj;
}
}
Here we have declared getInstance() static so that we can call it without instantiating the
class. The first time getInstance() is called it creates a new singleton object and after that
it just returns the same object. Note that Singleton obj is not created until we need it and
call getInstance() method. This is called lazy instantiation.
The main problem with above method is that it is not thread safe. Consider the following
execution sequence.
This execution sequence creates two objects for singleton. Therefore this classic implemen-
tation is not thread safe.
275
Chapter 49. Singleton Design Pattern | Implementation
Here using synchronized makes sure that only one thread at a time can execute getInstance().
The main disadvantage of this is method is that using synchronized every time while cre-
ating the singleton object is expensive and may decrease the performance of your program.
However if performance of getInstance() is not critical for your application this method
provides a clean and simple solution.
Here we have created instance of singleton in static initializer. JVM executes static initializer
when the class is loaded and hence this is guaranteed to be thread safe. Use this method only
when your singleton class is light and is used throughout the execution of your program.
276
Chapter 49. Singleton Design Pattern | Implementation
{
// To make thread safe
synchronized (Singleton.class)
{
// check again as multiple threads
// can reach above step
if (obj==null)
obj = new Singleton();
}
}
return obj;
}
}
We have declared the obj volatile which ensures that multiple threads offer the obj variable
correctly when it is being initialized to Singleton instance. This method drastically reduces
the overhead of calling the synchronized method every time.
References:
Head First Design Patterns book (Highly recommended)
https://en.wikipedia.org/wiki/Singleton_pattern
Source
https://www.geeksforgeeks.org/singleton-design-pattern/
277
Chapter 50
278
Chapter 50. Singleton Design Pattern | Introduction
1. It should have only one instance : This is done by providing instance of class
from within the class. Outer classes or subclasses should be prevented to create the
instance. This is done by making the constructor private in java so that no class can
access the constructor and hence cannot instantiate it.
2. Instance should be globally accessible : Instance of singleton class should be
globally accessible so that each class can use it. In java it is done by making the
access-specifier of instance public.
279
Chapter 50. Singleton Design Pattern | Introduction
This class also cannot be instantiated from application. Hence it is also a singleton
class.
280
Chapter 50. Singleton Design Pattern | Introduction
3. Configuration File: This is another potential candidate for Singleton pattern be-
cause this has a performance benefit as it prevents multiple users to repeatedly access
and read the configuration file or properties file. It creates a single instance of the con-
figuration file which can be accessed by multiple calls concurrently as it will provide
static config data loaded into in-memory objects. The application only reads from the
configuration file at the first time and there after from second call onwards the client
applications read the data from in-memory objects.
4. Cache: We can use the cache as a singleton object as it can have a global point of
reference and for all future calls to the cache object the client application will use the
in-memory object.
Important points
• Singleton classes can have only one instance and that instance should be globally
accessible.
• java.lang.Runtime and java.awt.Desktop are 2 singleton classes provided by JVM.
• Singleton Design pattern is a type of creational design pattern.
• Outer classes should be prevented to create instance of singleton class.
References:-
https://en.wikipedia.org/wiki/Singleton_pattern
https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html
https://docs.oracle.com/javase/7/docs/api/java/awt/Desktop.html
Source
https://www.geeksforgeeks.org/singleton-design-pattern-introduction/
281
Chapter 51
282
Chapter 51. State Design Pattern
283
Chapter 51. State Design Pattern
class StatePattern
{
public static void main(String[] args)
{
AlertStateContext stateContext = new AlertStateContext();
stateContext.alert();
stateContext.alert();
stateContext.setState(new Silent());
stateContext.alert();
stateContext.alert();
stateContext.alert();
}
}
Output:
vibration...
vibration...
silent...
silent...
silent...
• With State pattern, the benefits of implementing polymorphic behavior are evident,
and it is also easier to add states to support additional behavior.
• In the State design pattern, an object’s behavior is the result of the function of its
state, and the behavior gets changed at runtime depending on the state. This removes
the dependency on the if/else or switch/case conditional logic. For example, in the
TV remote scenario, we could have also implemented the behavior by simply writing
one class and method that will ask for a parameter and perform an action (switch the
TV on/off) with an if/else block.
• The State design pattern also improves Cohesion since state-specific behaviors are
aggregated into the ConcreteState classes, which are placed in one location in the
code.
• The State design pattern can be used when we need to change state of object at runtime
by inputting in it different subclasses of some State base class. This circumstance is
advantage and disadvantage in the same time, because we have a clear separate State
classes with some logic and from the other hand the number of classes grows up.
Source
https://www.geeksforgeeks.org/state-design-pattern/
284
Chapter 52
285
Chapter 52. Strategy Pattern | Set 1 (Introduction)
286
Chapter 52. Strategy Pattern | Set 1 (Introduction)
It’s much cleaner. We took out some actions (which some characters might not perform) out
of Fighterclass and made interfaces for them. That way only characters that are supposed
to jump will implement the JumpBehavior.
What are the problems with above design?
The main problem with the above design is code reuse. Since there is no default implemen-
tation of jump and roll behavior we may have code duplicity. You may have to rewrite the
same jump behavior over and over in many subclasses.
How can we avoid this?
What if we made JumpBehavior and RollBehavior classes instead of interface? Well
then we would have to use multiple inheritance that is not supported in many languages
due to many problems associated with it.
Here strategy pattern comes to our rescue. We will learn what the strategy pattern is and
then apply it to solve our problem.
Definition:
Wikipedia defines strategy pattern as:
287
Chapter 52. Strategy Pattern | Set 1 (Introduction)
“In computer programming, the strategy pattern (also known as the policy pattern) is
a software design pattern that enables an algorithm’s behavior to be selected at runtime. The
strategy pattern
Class Diagram:
Here we rely on composition instead of inheritance for reuse. Context is composed of a
Strategy. Instead of implementing a behavior the Context delegates it to Strategy. The
context would be the class that would require changing behaviors. We can change behavior
dynamically. Strategy is implemented as interface so that we can change behavior without
affecting our context.
We will have a clearer understanding of strategy pattern when we will use it to solve our
problem.
Advantages:
1. A family of algorithms can be defined as a class hierarchy and can be used interchange-
ably to alter application behavior without changing its architecture.
2. By encapsulating the algorithm separately, new algorithms complying with the same
interface can be easily introduced.
3. The application can switch strategies at run-time.
4. Strategy enables the clients to choose the required algorithm, without using a “switch”
statement or a series of “if-else” statements.
5. Data structures used for implementing the algorithm are completely encapsulated
in Strategy classes. Therefore, the implementation of an algorithm can be changed
without affecting the Context class.
288
Chapter 52. Strategy Pattern | Set 1 (Introduction)
Disadvantages:
1. The application must be aware of all the strategies to select the right one for the right
situation.
2. Context and the Strategy classes normally communicate through the interface specified
by the abstract Strategy base class. Strategy base class must expose interface for all
the required behaviours, which some concrete Strategy classes might not implement.
3. In most cases, the application configures the Context with the required Strategy object.
Therefore, the application needs to create and maintain two objects in place of one.
References:
Source
https://www.geeksforgeeks.org/strategy-pattern-set-1/
289
Chapter 53
290
Chapter 53. Strategy Pattern | Set 2 (Implementation)
The Fighter class will now delegate its kick and jump behavior instead of using kick and
jump methods defined in the Fighter class or its subclass.
291
Chapter 53. Strategy Pattern | Set 2 (Implementation)
After reworking the final class diagram would be (Click on image for better view):
292
Chapter 53. Strategy Pattern | Set 2 (Implementation)
Comparing our design to the definition of strategy pattern encapsulated kick and jump be-
haviors are two families of algorithms. And these algorithms are interchangeable as evident
in implementation.
Below is the Java implementation of the same.
293
Chapter 53. Strategy Pattern | Set 2 (Implementation)
294
Chapter 53. Strategy Pattern | Set 2 (Implementation)
295
Chapter 53. Strategy Pattern | Set 2 (Implementation)
296
Chapter 53. Strategy Pattern | Set 2 (Implementation)
Output :
Ken
Default Punch
Tornado Kick
Short Jump
Long Jump
References:
Head First Design Patterns
Source
https://www.geeksforgeeks.org/strategy-pattern-set-2/
297
Chapter 54
298
Chapter 54. Template Method Design Pattern
Source : Wikipedia
299
Chapter 54. Template Method Design Pattern
{
public boolean isGift;
public abstract void doSelect();
public abstract void doPayment();
public final void giftWrap()
{
try
{
System.out.println("Gift wrap successfull");
}
catch (Exception e)
{
System.out.println("Gift wrap unsuccessful");
}
}
public abstract void doDelivery();
public final void processOrder(boolean isGift)
{
doSelect();
doPayment();
if (isGift) {
giftWrap();
}
doDelivery();
}
}
class NetOrder extends OrderProcessTemplate
{
@Override
public void doSelect()
{
System.out.println("Item added to online shopping cart");
System.out.println("Get gift wrap preference");
System.out.println("Get delivery address.");
}
@Override
public void doPayment()
{
System.out.println
("Online Payment through Netbanking, card or Paytm");
300
Chapter 54. Template Method Design Pattern
}
@Override
public void doDelivery()
{
System.out.println
("Ship the item through post to delivery address");
}
}
class StoreOrder extends OrderProcessTemplate
{
@Override
public void doSelect()
{
System.out.println("Customer chooses the item from shelf.");
}
@Override
public void doPayment()
{
System.out.println("Pays at counter through cash/POS");
}
@Override
public void doDelivery()
{
System.out.println("Item deliverd to in delivery counter.");
}
}
class TemplateMethodPatternClient
{
public static void main(String[] args)
{
OrderProcessTemplate netOrder = new NetOrder();
netOrder.processOrder(true);
System.out.println();
OrderProcessTemplate storeOrder = new StoreOrder();
storeOrder.processOrder(true);
}
}
Output :
301
Chapter 54. Template Method Design Pattern
The above example deals with order processing flow. The OrderProcessTemplate class is an
abstract class containing the algorithm skeleton. As shown on note, processOrder() is the
method that contains the process steps. We have two subclasses NetOrder and StoreOrder
which has the same order processing steps.
So the overall algorithm used to process an order is defined in the base class and used by
the subclasses. But the way individual operations are performed vary depending on the
subclass.
When to use template method
The template method is used in frameworks, where each implements the invariant parts of
a domain’s architecture, leaving “placeholders” for customization options.
The template method is used for the following reasons :
Reference :
Wikipedia
Source
https://www.geeksforgeeks.org/template-method-design-pattern/
302
Chapter 55
303
Chapter 55. The Decorator Pattern | Set 2 (Introduction and Design)
What we get in the end is a pizza with cheeseburst and capsicum toppings. Visualize the
“decorator” objects like wrappers. Here are some of the properties of decorators:
• Decorators have the same super type as the object they decorate.
• You can use multiple decorators to wrap an the object.
304
Chapter 55. The Decorator Pattern | Set 2 (Introduction and Design)
• Since decorators have same type as object, we can pass around decorated object instead
of original.
• We can decorate objects at runtime.
Definition:
The decorator pattern attaches additional responsibilities to an object dynamically. Decora-
tors provide a flexible alternative to subclassing for extending functionality.
Advantages:
• The decorator pattern can be used to make it possible to extend (decorate) the func-
tionality of a certain object at runtime.
• The decorator pattern is an alternative to subclassing. Subclassing adds behavior at
compile time, and the change affects all instances of the original class; decorating can
provide new behavior at runtime for individual objects.
• Decorator offers a pay-as-you-go approach to adding responsibilities. Instead of trying
to support all foreseeable features in a complex, customizable class, you can define a
simple class and add functionality incrementally with Decorator objects.
Disadvantages:
• Decorators can complicate the process of instantiating the component because you not
only have to instantiate the component, but wrap it in a number of decorators.
• It can be complicated to have decorators keep track of other decorators, because to
look back into multiple layers of the decorator chain starts to push the decorator
pattern beyond its true intent.
305
Chapter 55. The Decorator Pattern | Set 2 (Introduction and Design)
References:
Source
https://www.geeksforgeeks.org/the-decorator-pattern-set-2-introduction-and-design/
306
Chapter 56
307
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
Brevity is the soul of wit. We need to convey a lot of information with clarity and make
sure it is short. So an activity diagram helps people on both sides i.e. Businessmen and
Developers to interact and understand systems.
A question arises:
Do we need to use both the diagram and the textual documentation?
Different individuals have different preferences in which they understand something. For
example: To understand a concept, some people might prefer a written tutorial with images
while others would prefer a video lecture.
So we generally use both the diagram and the textual documentation to make our system
description as clear as possible. We also need to be sensitive to the needs of the audience
that we are catering to at times.
Difference between a Use case diagram and an Activity diagram
An activity diagram is used to model the workflow depicting conditions, constraints, sequen-
tial and concurrent activities. On the other hand, the purpose of a Use Case is to just depict
the functionality i.e. what the system does and not how it is done. So in simple terms, an
activity diagram shows ‘How’ while a Use case shows ‘What’ for a particular system.
The levels of abstraction also vary for both of them. An activity diagram can be used to
illustrate a business process (high level implementation) to a stand alone algorithm (ground
level implementation). However, Use cases have a low level of abstraction. They are used
to show a high level of implementation only.
308
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
1. Initial State – The starting state before an activity takes place is depicted using the
initial state.
309
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
For example – Here the initial state is the state of the system before the application
is opened.
310
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
311
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
312
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
313
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
8. Merge or Merge Event – Scenarios arise when activities which are not being exe-
cuted concurrently have to be merged. We use the merge notation for such scenarios.
We can merge two or more activities into one if the control proceeds onto the next
activity irrespective of the path chosen.
314
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
315
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
316
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
317
Chapter 56. Unified Modeling Language (UML) | Activity Diagrams
References –
Activity diagrams – IBM
Activity Diagram – sparxsystems
Source
https://www.geeksforgeeks.org/unified-modeling-language-uml-activity-diagrams/
318
Chapter 57
• Complex applications need collaboration and planning from multiple teams and hence
require a clear and concise way to communicate amongst them.
• Businessmen do not understand code. So UML becomes essential to communicate with
non programmers essential requirements, functionalities and processes of the system.
• A lot of time is saved down the line when teams are able to visualize processes, user
interactions and static structure of the system.
UML is linked with object oriented design and analysis. UML makes the use of elements
and forms associations between them to form diagrams. Diagrams in UML can be broadly
classified as:
319
Chapter 57. Unified Modeling Language (UML) | An Introduction
The image below shows the hierarchy of diagrams according to UML 2.2
1. Class – A class defines the blue print i.e. structure and functions of an object.
2. Objects – Objects help us to decompose large systems and help us to modularize
our system. Modularity helps to divide our system into understandable components
so that we can build our system piece by piece. An object is the fundamental unit
(building block) of a system which is used to depict an entity.
3. Inheritance – Inheritance is a mechanism by which child classes inherit the properties
of their parent classes.
4. Abstraction – Mechanism by which implementation details are hidden from user.
5. Encapsulation – Binding data together and protecting it from the outer world is
referred to as encapsulation.
6. Polymorphism – Mechanism by which functions or entities are able to exist in
different forms.
• Software development methodologies like agile have been incorporated and scope of
original UML specification has been broadened.
• Originally UML specified 9 diagrams. UML 2.x has increased the number of diagrams
from 9 to 13. The four diagrams that were added are : timing diagram, communication
diagram, interaction overview diagram and composite structure diagram. UML 2.x
renamed statechart diagrams to state machine diagrams.
• UML 2.x added the ability to decompose software system into components and sub-
components.
320
Chapter 57. Unified Modeling Language (UML) | An Introduction
1. Class Diagram – The most widely use UML diagram is the class diagram. It is
the building block of all object oriented software systems. We use class diagrams to
depict the static structure of a system by showing system’s classes,their methods and
attributes. Class diagrams also help us identify relationship between different classes
or objects.
2. Composite Structure Diagram – We use composite structure diagrams to repre-
sent the internal structure of a class and its interaction points with other parts of
the system. A composite structure diagram represents relationship between parts and
their configuration which determine how the classifier (class, a component, or a de-
ployment node) behaves. They represent internal structure of a structured classifier
making the use of parts, ports, and connectors. We can also model collaborations
using composite structure diagrams. They are similar to class diagrams except they
represent individual parts in detail as compared to the entire class.
3. Object Diagram – An Object Diagram can be referred to as a screenshot of the
instances in a system and the relationship that exists between them. Since object
diagrams depict behaviour when objects have been instantiated, we are able to study
the behaviour of the system at a particular instant. An object diagram is similar to a
class diagram except it shows the instances of classes in the system. We depict actual
classifiers and their relationships making the use of class diagrams. On the other hand,
an Object Diagram represents specific instances of classes and relationships between
them at a point of time.
4. Component Diagram – Component diagrams are used to represent the how the
physical components in a system have been organized. We use them for modelling im-
plementation details. Component Diagrams depict the structural relationship between
software system elements and help us in understanding if functional requirements have
been covered by planned development. Component Diagrams become essential to use
when we design and build complex systems. Interfaces are used by components of the
system to communicate with each other.
5. Deployment Diagram – Deployment Diagrams are used to represent system hard-
ware and its software.It tells us what hardware components exist and what software
components run on them.We illustrate system architecture as distribution of software
artifacts over distributed targets. An artifact is the information that is generated by
system software. They are primarily used when a software is being used, distributed
or deployed over multiple machines with different configurations.
6. Package Diagram – We use Package Diagrams to depict how packages and their
elements have been organized. A package diagram simply shows us the dependencies
between different packages and internal composition of packages. Packages help us
to organise UML diagrams into meaningful groups and make the diagram easy to
understand. They are primarily used to organise class and use case diagrams.
321
Chapter 57. Unified Modeling Language (UML) | An Introduction
execution of a use case. We model sequential and concurrent activities using activity
diagrams. So, we basically depict workflows visually using an activity diagram.An
activity diagram focuses on condition of flow and the sequence in which it happens.
We describe or depict what causes a particular event using an activity diagram.
3. Use Case Diagrams – Use Case Diagrams are used to depict the functionality of
a system or a part of a system. They are widely used to illustrate the functional
requirements of the system and its interaction with external agents(actors). A use
case is basically a diagram representing different scenarios where the system can be
used. A use case diagram gives us a high level view of what the system or a part of
the system does without going into implementation details.
4. Sequence Diagram – A sequence diagram simply depicts interaction between objects
in a sequential order i.e. the order in which these interactions take place.We can
also use the terms event diagrams or event scenarios to refer to a sequence diagram.
Sequence diagrams describe how and in what order the objects in a system function.
These diagrams are widely used by businessmen and software developers to document
and understand requirements for new and existing systems.
5. Communication Diagram – A Communication Diagram(known as Collaboration
Diagram in UML 1.x) is used to show sequenced messages exchanged between objects.
A communication diagram focuses primarily on objects and their relationships. We
can represent similar information using Sequence diagrams,however, communication
diagrams represent objects and links in a free form.
6. Timing Diagram – Timing Diagram are a special form of Sequence diagrams which
are used to depict the behavior of objects over a time frame. We use them to show
time and duration constraints which govern changes in states and behavior of objects.
7. Interaction Overview Diagram – An Interaction Overview Diagram models a
sequence of actions and helps us simplify complex interactions into simpler occurrences.
It is a mixture of activity and sequence diagrams.
Reference –
Unified Modeling Language – Wikipedia
Unified Modeling Language – IBM
Source
https://www.geeksforgeeks.org/unified-modeling-language-uml-introduction/
322
Chapter 58
• Simplifies complex software design, can also implement OOPs like concept which is
widely used.
• It reduces thousands of words of explanation in a few graphical diagrams that may
reduce time consumption to understand.
• It makes communication more clear and real.
• It helps to acquire the entire system in a view.
• It becomes very much easy for the software programmer to implement the actual
demand once they have the clear picture of the problem.
Types of UML: The UML diagrams are divided into two parts: Structural UML diagrams
and Behavioral UML diagrams which are listed below:
323
Chapter 58. Unified Modeling Language (UML) | Class Diagrams
• Activity diagram
• Sequence diagram
• Use case diagram
• State diagram
• Communication diagram
• Interaction overview diagram
• Timing diagram
UML class diagrams: Class diagrams are the main building blocks of every object ori-
ented methods. The class diagram can be used to show the classes, relationships, interface,
association, and collaboration. UML is standardized in class diagrams. Since classes are
the building block of an application that is based on OOPs, so as the class diagram has ap-
propriate structure to represent the classes, inheritance, relationships, and everything that
OOPs have in its context. It describes various kinds of objects and the static relationship
in between them.
The main purpose to use class diagrams are:
• This is the only UML which can appropriately depict various aspects of OOPs concept.
• Proper design and analysis of application can be faster and efficient.
• It is base for deployment and component diagram.
There are several software available which can be used online and offline to draw these
diagrams Like Edraw max, lucid chart etc. There are several points to be kept in focus
while drawing the class diagram. These can be said as its syntax:
Below is the example of Animal class (parent) having two child class as dog and cat both
have object d1, c1 inheriting properties of the parent class.
324
Chapter 58. Unified Modeling Language (UML) | Class Diagrams
import java.io.*;
class GFG {
public static void main(String[] args)
{
dog d1 = new dog();
d1.bark();
d1.run();
cat c1 = new cat();
c1.meww();
}
}
class Animal {
public void run()
{
String name;
String colour;
System.out.println("animal is running");
}
}
325
Chapter 58. Unified Modeling Language (UML) | Class Diagrams
class dog extends Animal {
public void bark()
{
System.out.println("wooh!wooh! dog is barking");
}
public void run()
{
System.out.println("dog is running");
}
}
class cat extends Animal {
public void meww()
{
System.out.println("meww! meww!");
}
}
Process to design class diagram: In Edraw max (or any other platform where class
diagrams can be drawn) follow the steps:
There are several diagram components which can be efficiently used while making/editing
the model. These are as follows:
326
Chapter 58. Unified Modeling Language (UML) | Class Diagrams
• Interface
• Relationships {inheritance, association, generalization}
• Associations {bidirectional, unidirectional}
Class diagrams are one of the most widely used diagrams in the fields of software engineering
as well as businesses modelling.
Source
https://www.geeksforgeeks.org/unified-modeling-language-uml-class-diagrams/
327
Chapter 59
328
Chapter 59. Unified Modeling Language (UML) | Object Diagrams
329
Chapter 59. Unified Modeling Language (UML) | Object Diagrams
Notation Meaning
0..1 Zero or one
1 One only
0..* Zero or more
Zero or more
1..* One or more
7 Seven only
0..2 Zero or two
4..7 Four to seven
330
Chapter 59. Unified Modeling Language (UML) | Object Diagrams
331
Chapter 59. Unified Modeling Language (UML) | Object Diagrams
legs and arms. Here legs and arms cant exist without the existence of their parent
object. So whenever independent existence of the child is not possible we use a com-
position relationship. We use a filled diamond on the containing object with a line
which joins it to the contained object.
Association and dependency are often confused in their usage. A source of confusion was
the use of transient links in UML 1. Meta-models are now handled differently in UML 2
and the issue has been resolved.
There are a large number of dependencies in a system. We only represent the ones which
are essential to convey for understanding the system. We need to understand that every
association implies a dependency itself. We , however, prefer not to draw it separately. An
association implies a dependency similar to a way in which generalization does.
332
Chapter 59. Unified Modeling Language (UML) | Object Diagrams
Source
https://www.geeksforgeeks.org/unified-modeling-language-uml-object-diagrams/
333
Chapter 60
334
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
335
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
standard in UML for naming a lifeline follows the following format – Instance Name :
Class Name
Figure – lifeline
We display a lifeline in a rectangle called head with its name and type. The head is
located on top of a vertical dashed line (referred to as the stem) as shown above. If
we want to model an unnamed instance, we follow the same pattern except now the
portion of lifeline’s name is left blank.
Difference between a lifeline and an actor – A lifeline always portrays an object
internal to the system whereas actors are used to depict objects external to the system.
The following is an example of a sequence diagram:
336
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
337
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
338
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
339
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
For example – In the scenario below when the order is received by the user, the
object of order class can be destroyed.
340
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
341
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
342
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
343
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
344
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
4. Guards – To model conditions we use guards in UML. They are used when we need
to restrict the flow of messages on the pretext of a condition being met. Guards play
an important role in letting software developers know the constraints attached to a
system or a particular process.
For example: In order to be able to withdraw cash, having a balance greater than
zero is a condition that must be met as shown below.
345
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
346
Chapter 60. Unified Modeling Language (UML) | Sequence Diagrams
The above sequence diagram depicts the sequence diagram for an emotion based music
player:
• Used to model and visualise the logic behind a sophisticated function, operation or
procedure.
• They are also used to show details of UML use case diagrams.
• Used to understand the detailed functionality of current or future systems.
• Visualise how messages and tasks move between objects or components in a system.
References –
The sequence diagram – IBM
Sequence Diagram – sparxsystems
Source
https://www.geeksforgeeks.org/unified-modeling-language-uml-sequence-diagrams/
347
Chapter 61
• We use it to state the events responsible for change in state (we do not show what
processes cause those events).
• We use it to model the dynamic behavior of the system .
• To understand the reaction of objects/classes to internal or external stimuli.
Firstly let us understand what are Behavior diagrams? There are two types of diagrams
in UML :
1. Structure Diagrams – Used to model the static structure of a system, for example-
class diagram, package diagram, object diagram, deployment diagram etc.
2. Behavior diagram – Used to model the dynamic change in the system over time.
They are used to model and construct the functionality of a system. So, a behavior
diagram simply guides us through the functionality of the system using Use case
diagrams, Interaction diagrams, Activity diagrams and State diagrams.
348
Chapter 61. Unified Modeling Language (UML) | State Diagrams
not the processes or commands causing the changes. However, a flowchart on the other
hand portrays the processes or commands that on execution change the state of class or an
object of the class.
1. Initial state – We use a black filled circle represent the initial state of a System or
a class.
Figure – transition
3. State – We use a rounded rectangle to represent a state. A state represents the
conditions or circumstances of an object of a class at an instant of time.
349
Chapter 61. Unified Modeling Language (UML) | State Diagrams
states. We use the fork notation to represent a state splitting into two or more con-
current states.
5. Join – We use a rounded solid rectangular bar to represent a Join notation with
incoming arrows from the joining states and outgoing arrow towards the common goal
state. We use the join notation when two or more states concurrently converge into
one on the occurrence of an event or events.
6. Self transition – We use a solid arrow pointing back to the state itself to represent a
self transition. There might be scenarios when the state of the object does not change
upon the occurrence of an event. We use self transitions to represent such cases.
350
Chapter 61. Unified Modeling Language (UML) | State Diagrams
8. Final state – We use a filled circle within a circle notation to represent the final state
in a state machine diagram.
351
Chapter 61. Unified Modeling Language (UML) | State Diagrams
1. On the event of an order being received, we transit from our initial state to Unprocessed
order state.
2. The unprocessed order is then checked.
3. If the order is rejected, we transit to the Rejected Order state.
4. If the order is accepted and we have the items available we transit to the fulfilled order
state.
5. However if the items are not available we transit to the Pending Order state.
352
Chapter 61. Unified Modeling Language (UML) | State Diagrams
6. After the order is fulfilled, we transit to the final state. In this example, we merge the
two states i.e. Fulfilled order and Rejected order into one final state.
Note – Here we could have also treated fulfilled order and rejected order as final states
separately.
Reference –
State Diagram – IBM
Source
https://www.geeksforgeeks.org/unified-modeling-language-uml-state-diagrams/
353
Chapter 62
• a method called Visit() which is implemented by the visitor and is called for every
element in the data structure
• visitable classes providing Accept() methods that accept a visitor
354
Chapter 62. Visitor design pattern
Design components
• Client : The Client class is a consumer of the classes of the visitor design pattern. It
has access to the data structure objects and can instruct them to accept a Visitor to
perform the appropriate processing.
• Visitor : This is an interface or an abstract class used to declare the visit operations
for all the types of visitable classes.
• ConcreteVisitor : For each type of visitor all the visit methods, declared in abstract
visitor, must be implemented. Each Visitor will be responsible for different operations.
• Visitable : is an interface which declares the accept operation. This is the entry
point which enables an object to be “visited” by the visitor object.
• ConcreteVisitable : Those classes implements the Visitable interface or class and
defines the accept operation. The visitor object is passed to this object using the
accept operation.
interface ItemElement
{
public int accept(ShoppingCartVisitor visitor);
}
class Book implements ItemElement
{
private int price;
private String isbnNumber;
355
Chapter 62. Visitor design pattern
public Book(int cost, String isbn)
{
this.price=cost;
this.isbnNumber=isbn;
}
public int getPrice()
{
return price;
}
public String getIsbnNumber()
{
return isbnNumber;
}
@Override
public int accept(ShoppingCartVisitor visitor)
{
return visitor.visit(this);
}
}
class Fruit implements ItemElement
{
private int pricePerKg;
private int weight;
private String name;
public Fruit(int priceKg, int wt, String nm)
{
this.pricePerKg=priceKg;
this.weight=wt;
this.name = nm;
}
public int getPricePerKg()
{
return pricePerKg;
}
public int getWeight()
{
return weight;
}
356
Chapter 62. Visitor design pattern
357
Chapter 62. Visitor design pattern
class ShoppingCartClient
{
public static void main(String[] args)
{
ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),
new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
int total = calculatePrice(items);
System.out.println("Total Cost = "+total);
}
private static int calculatePrice(ItemElement[] items)
{
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item : items)
{
sum = sum + item.accept(visitor);
}
return sum;
}
}
Output:
Here, in the implementation if accept() method in all the items are same but it can be
different, for example there can be logic to check if item is free then don’t call the visit()
method at all.
Advantages :
• If the logic of operation changes, then we need to make change only in the visitor
implementation rather than doing it in all the item classes.
• Adding a new item to the system is easy, it will require change only in visitor interface
and implementation and existing item classes will not be affected.
Disadvantages :
358
Chapter 62. Visitor design pattern
• We should know the return type of visit() methods at the time of designing otherwise
we will have to change the interface and all of its implementations.
• If there are too many implementations of visitor interface, it makes it hard to extend.
Source
https://www.geeksforgeeks.org/visitor-design-pattern/
359