Design Patterns: Abdulhakim Sirko
Design Patterns: Abdulhakim Sirko
(With Examples)
singleton
prototype
factory
session
observer
adapter
decorator
facade
ABDULHAKIM SIRKO
@abdulhakimsirko
1. Singleton Design Pattern
The singleton pattern is a design pattern that ensures a class has only one instance and provides
a global point of access to that instance. The basic idea behind the pattern is to create a single
instance of a class, and to provide a global point of access to that instance.
• The singleton pattern ensures that a class has only one instance and provides a
global point of access to that instance.
• The singleton pattern uses a private constructor and a static method to ensure that
only one instance of the class is created.
• The singleton pattern provides a global point of access to the single instance of the
class, typically by using a static property or method.
• It ensures that a class has only one instance and provides a global point of access to
that instance.
• It can be used to control access to resources that are shared across the system, such
as a database connection or a configuration object.
• It can be used to implement caching, logging, and other system-wide services.
Example:
In this example, the Singleton class has a private constructor, which prevents it from being
instantiated from outside the class. The getInstance() method is a public static method that
returns a single instance of the Singleton class. The instance variable is declared as private and
static so that it can only be accessed through the getInstance() method.
The getInstance() method checks if the instance variable is null, and if it is, it creates a new
instance of the Singleton class. If the instance variable is not null, it simply returns the existing
instance.
This way, there will always be only one instance of the Singleton class, and it can be accessed
from anywhere in the code by calling the getInstance() method.
The prototype pattern is a design pattern that allows you to create new objects by copying an
existing object, rather than creating new objects by using a class's constructor. The basic idea
behind the pattern is that you create a prototype of an object, and then use that prototype to
create new instances of the object.
• The prototype pattern allows you to create new objects by copying an existing object,
rather than creating new objects using a class's constructor.
• It allows you to create new objects more quickly and easily than if you were to use a
class's constructor.
• It allows you to create new objects that are copies of existing objects, rather than
creating new objects that are completely independent.
• It allows you to create new objects that have the same state as the prototype but can be
modified independently.
The prototype pattern can be implemented in many ways, but one common way is to create an
interface called Clonable or Prototype that defines a method for creating a copy of the object.
Classes that want to support being copied can implement this interface and provide an
implementation for the method.
Example:
In this example, the Person class implements the Clonable interface, and provides an
implementation of the clone() method that creates a new Person object that is a copy of the
original object. The prototype pattern can be useful in a variety of situations, some common
use cases are:
The factory pattern is a design pattern that provides a way to create objects without specifying
the exact class of object that will be created. The basic idea behind the pattern is that you
define a factory class that has a method for creating objects, but the factory class does not
know or care about the specific class of object that will be created. Instead, it relies on a set of
rules or a configuration to determine what class of object to create.
• The factory pattern provides a way to create objects without specifying the exact class
of object that will be created.
• The factory class has a method for creating objects, but the factory class does not know
or care about the specific class of object that will be created.
• The factory class relies on a set of rules or a configuration to determine what class of
object to create.
There are several types of factory pattern, but the most common one is the "Factory Method"
pattern which defines an interface for creating an object, but let subclasses to alter the class of
objects that will be created.
• It allows you to create objects without specifying the exact class of object that will be
created, which can make your code more flexible and easier to change.
• It allows you to encapsulate the process of creating objects, making it easier to change
the way objects are created in the future, without affecting the code that uses the
factory.
• It can make your code more reusable, as you can use the factory to create objects in a
variety of contexts.
An example of factory pattern could be a class that creates different types of vehicles like
cars, trucks, and buses.
In this example, the VehicleFactory class is the factory class, it has a method called getVehicle()
that creates objects of different types of vehicle based on the type passed in as an argument.
The IVehicle interface is the factory method, it defines the interface for creating the objects
and the concrete classes Car, Truck, Bus are the objects that implement the factory method.
This way, the code that uses the factory doesn't need to know or care about the specific classes
of objects that are being created, as long as they implement the IVehicle interface.
The session design pattern provides a way to store and retrieve session data, and to keep track
of a user's session throughout the lifetime of an application. It is typically used in web
applications, where a user's session state needs to be maintained across multiple HTTP
requests.
• The session design pattern provides a way to store and retrieve session data, and to
keep track of a user's session throughout the lifetime of an application.
• The session object is responsible for managing the state of a user's session within an
application, and can be stored in memory, on the client side using cookies, or on the
server side using a session ID.
• The session object can be used to store information about a user's authentication
status, their preferences, the items in their shopping cart, etc.
In this example, the Session class is implemented as a singleton, it has methods for logging in,
logging out, checking authentication status and getting the user.
The class implements a private constructor, which ensures that only one instance of the class
can be created, and a static method for retrieving the single instance.
This way, the session object can be accessed from any part of the application, in order to check
the user's authentication status, retrieve their user object and maintain their session
throughout the application.
5. Session design pattern
The observer pattern is a design pattern that allows objects to be notified of changes in the
state of other objects, without the objects being tightly coupled to one another. The basic idea
behind the pattern is to create a mechanism for objects to "observe" other objects and to be
notified when the state of the observed objects changes.
• The observer pattern allows objects to be notified of changes in the state of other
objects, without the objects being tightly coupled to one another.
• The observer pattern is based on the publish-subscribe model, where objects "publish"
notifications of changes in their state, and other objects "subscribe" to receive those
notifications.
• The observer pattern uses interfaces to define the "publish" and "subscribe" methods,
allowing different classes to implement them in different ways.
• It allows objects to be notified of changes in the state of other objects without being
tightly coupled to them.
• It allows objects to be notified of changes in the state of other objects even if they don't
know the other objects' classes.
An example of observer pattern could be an application that notifies the user when the stock
price of a specific company has changed.
This example shows how the Observer pattern can be implemented to keep track of the stock
prices of a company.
There are two main classes: StockPriceObserver and StockPriceSubject, which represent the
observer and the subject, respectively.
The StockPriceSubject maintains a list of observers and notifies them whenever there is a change
in the stock price.
The example defines an interface Observer with a single method update(), and an interface
Subject with three methods registerObserver(), removeObserver(), and notifyObservers().
The StockPriceObserver class implements the Observer interface, while the StockPriceSubject
class implements the Subject interface.
Whenever the price of a stock changes, the setPrice() method is called on the StockPriceSubject
object, which updates the price and then notifies all the registered observers by calling their
update() method.
The StockPriceObserver objects, in turn, update their price and call their display() method to
show the current stock price.
• The adapter pattern allows incompatible classes to work together by converting the
interface of one class into an interface that another class expects.
• The adapter pattern uses an "adapter" class that can adapt the interface of an existing
class to the interface that is expected by other classes.
• The adapter pattern can be used to create a link between classes that couldn't
otherwise be linked due to incompatible interfaces.
• It allows incompatible classes to work together by adapting one class's interface to the
interface that another class expects.
• It promotes loose coupling between classes, making it easier to change the
implementation of one class without affecting the classes that use it.
Example:
An example of adapter pattern could be a situation where a class called Square wants to reuse
another class Rectangle but the problem is that Square expects to have a method
calculateArea and Rectangle has the methods calculateArea(length: double, width:double).
In this example, the Rectangle class has a method calculateArea(length: double, width:double)
but Square class expects to have a method calculateArea().
The RectangleAdapter class implements the Shape interface and adapt the Rectangle class to
the expected interface by square class, by implementing calculateArea() that internally calls
calculateArea(length: double, width:double).
This way the Square class can use the rectangle class through the Adapter class and both classes
can work together.
• The decorator pattern uses a "decorator" class that can be used to add or override
behavior for an existing class.
• The decorator pattern can be used to add new behavior to an existing class without the
need to create a new subclass.
There are a number of benefits to using the decorator pattern:
Example:
Let's say we have an interface called Coffee that defines the basic behavior of a coffee object:
Now, let's say we want to create some decorator classes that will add extra functionality to a
Coffee object. For example, we could create a Milk decorator that adds milk to the coffee:
In this example, the Milk decorator takes a Coffee object in its constructor and stores it in an
instance variable. The getCost() method returns the cost of the Coffee object passed in, plus
the cost of the milk (0.5). The getDescription() method returns the description of the Coffee
object passed in, plus the string "milk".
The facade pattern is often used in situations where a system is made up of many different
components, each with its own interface and set of responsibilities. By using a facade, you can
create a single, unified interface that makes it easier for other parts of the system to interact
with the system as a whole.
• The facade hides the complexity of the underlying system and makes it easier to use.
• The facade can be used to decouple the other parts of the system from the details of
the underlying system.
• It makes the system easier to use, as the simplified interface is easier to understand
than the complexity of the underlying system.
• It makes the system more flexible, as changes to the underlying system can be hidden
behind the facade.
• It makes the system more maintainable, as changes to the underlying system do not
affect the other parts of the system that use the facade.
The facade pattern is a commonly used pattern in many different programming languages and
environments. It can be implemented in many ways, and the specific implementation details
depend on the programming language and the system being built.
Example:
Let's say we have a complex subsystem consisting of multiple classes that need to be used
together to accomplish a task. Instead of exposing the complexity of the subsystem to the client
code, we can create a facade that provides a simple interface for the client to interact with the
subsystem.
In this example, we have three subsystem classes, SubsystemClassA, SubsystemClassB, and
SubsystemClassC, each with their own set of methods.
We also have a Facade class that provides a simple interface for the client code to interact with
the subsystem.
The Facade class has instances of each of the subsystem classes, and a single public method
called doTask() that calls the necessary methods of each subsystem class in the correct order to
accomplish the task.
By using the facade pattern, the complexity of the subsystem is hidden from the client code,
which only needs to interact with the simple doTask() method of the Facade class.