0% found this document useful (0 votes)
20 views12 pages

Overview of Design Patterns in Programming

Design patterns are reusable solutions to common programming problems. The document discusses several design patterns including Singleton, Factory, Adapter, Bridge, Proxy, and Flyweight. The Singleton pattern ensures a class has only one instance. The Factory pattern provides an interface for creating objects without specifying the exact class. The Adapter pattern allows incompatible interfaces to work together.

Uploaded by

Anonymous Racoon
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views12 pages

Overview of Design Patterns in Programming

Design patterns are reusable solutions to common programming problems. The document discusses several design patterns including Singleton, Factory, Adapter, Bridge, Proxy, and Flyweight. The Singleton pattern ensures a class has only one instance. The Factory pattern provides an interface for creating objects without specifying the exact class. The Adapter pattern allows incompatible interfaces to work together.

Uploaded by

Anonymous Racoon
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Design Patterns

Design Patterns: Design patterns are typical solutions to commonly occurring problems in software
design. They are like pre-made blueprints that you can customize to solve a recurring design problem in
your code.

They were first introduced in the book Design Patterns: Elements of Reusable Object-Oriented
Software, published in 1994. The book was written by Erich Gamma, Richard Helm, Ralph Johnson, and
John Vlissides, collectively known as Gang of Four. The design patterns in this book are also commonly
known as GoF design patterns.

Design Pattern Types


Singleton
Singleton is a creational design pattern that lets you ensure that a class has only one instance while
providing a global access point to this instance.
The Singleton pattern solves two problems at the same time
1. Ensure that a class has just a single instance
2. Provide a global access point to that instance
Solution

1. Make the default constructor private, to prevent other objects from using the new operator with
the Singleton class.
2. Create a static creation method that acts as a constructor.

class Singleton {

private static Singleton ins = null;


private Singleton(){}
public static Singleton getInstance()
{
if (ins == null)
ins = new Singleton();

return ins;
}
}
class GFG {
public static void main(String args[])
{
Singleton x = [Link]();
Singleton y = [Link]();
Singleton z = [Link]();
[Link]("Hashcode of x is "
+ [Link]());
[Link]("Hashcode of y is "
+ [Link]());
[Link]("Hashcode of z is "
+ [Link]());
}
}
Output:
Hashcode of x is 558638686
Hashcode of y is 558638686
Hashcode of z is 558638686
Factory

The Factory Design Pattern is a creational design pattern that provides an interface for creating objects in
a super class but allows subclasses to alter the type of objects that will be created.

public interface Shape {


void draw();
}

public class Rectangle implements Shape {

@Override
public void draw() {
[Link]("Inside Rectangle::draw() method.");
}
}

public class Square implements Shape {

@Override
public void draw() {
[Link]("Inside Square::draw() method.");
}
}
public class ShapeFactory {
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}

if([Link]("RECTANGLE")){
return new Rectangle();

} else if([Link]("SQUARE")){
return new Square();
}

return null;
}
}

public class FactoryPatternDemo {

public static void main(String[] args) {


ShapeFactory shapeFactory = new ShapeFactory();
Shape shape1 = [Link]("CIRCLE");[Link]();
Shape shape2 = [Link]("RECTANGLE");[Link]();
}
}

Output
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Adapter
The adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.

// Target
interface Railroad{
void vehicleMoving();
}
// Adaptee (the class we want to adapt)
class Car {
public void Drive() {// drive the car}
}
}
// Adapter class implementing the Target interface
class Adapter implements Target {
private Car car;
public Adapter(Car car) {[Link] = car;}
@Override
public void vehicleMoving() {[Link]();}
}
// Client code
public class AdapterPatternExample {
public static void main(String[] args) {
Car car= new Car();
Railroad railroad = new Adapter(car);
[Link]();
}
}
Bridge
Bridge is a structural design pattern that lets you split a large class or a set of closely related classes into
two separate hierarchies—abstraction and implementation—which can be developed independently of
each other.

// Implementor: Color
interface Color {
String applyColor();
}
// Concrete Implementor: RedColor
class RedColor implements Color {
@Override
public String applyColor() {return "Red";}
}
// Concrete Implementor: BlueColor
class BlueColor implements Color {
@Override
public String applyColor() {return "blue";}
}
// Abstraction: Shape
abstract class Shape {
protected Color color;
public Shape(Color color) {[Link] = color;}
abstract String draw();
}
// Concrete Abstraction: Circle
class Circle extends Shape {
public Circle(Color color) { super(color);}
@Override
String draw() {
return "Drawing a Circle with color " + [Link]();
}
}

// Concrete Abstraction: Square


class Square extends Shape {
public Square(Color color) {
super(color);
}

@Override
String draw() {
return "Drawing a Square with color " + [Link]();
}
}

// Usage
public class BridgePatternExample {
public static void main(String[] args) {
Color redColor = new RedColor();
Color blueColor = new BlueColor();

Circle circle = new Circle(redColor);


Square square = new Square(blueColor);

[Link]([Link]()); // Output: Drawing a Circle with


color Red
[Link]([Link]()); // Output: Drawing a Square with
color Blue
}
}
Proxy
Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object. A
proxy controls access to the original object.

public interface Image {


void display();
}
public class RealImage implements Image {

private String fileName;

public RealImage(String fileName){


[Link] = fileName;
loadFromDisk(fileName);
}

@Override
public void display() {
[Link]("Displaying " + fileName);
}

private void loadFromDisk(String fileName){


[Link]("Loading " + fileName);
}
}
public class ProxyImage implements Image{

private RealImage realImage;


private String fileName;

public ProxyImage(String fileName){


[Link] = fileName;
}

@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
[Link]();
}
}
public class ProxyPatternDemo {

public static void main(String[] args) {


Image image = new ProxyImage("test_10mb.jpg");

//image will be loaded from disk


[Link]();
[Link]("");

//image will not be loaded from disk


[Link]();
}
}

Output
Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg
Flyweight
Flyweight is a structural design pattern that lets you fit more objects into the available amount of
RAM by sharing common parts of the state between multiple objects instead of keeping all of the
data in each object.

interface Shape {
void draw();
} bullet
class Circle implements Shape {
private String color;

public Circle(String color) {


[Link] = color;
}

@Override
public void draw() {
[Link]("Drawing Circle with color: " + color);
}
}
// Flyweight Factory
class ShapeFactory {
private static final Map<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle) [Link](color);

if (circle == null) {
circle = new Circle(color);
[Link](color, circle);
[Link]("Creating new Circle with color: " + color);
}

else {
[Link]("Returning the already existing circle of
color: " + color);

}
return circle;
}
}

// Client
public class FlyweightPatternExample {
public static void main(String[] args) {
// Using the flyweight factory to get circles with different colors
Shape redCircle = [Link]("Red");
Shape greenCircle = [Link]("Green");
Shape blueCircle = [Link]("Blue");
Shape redCircleAgain = [Link]("Red"); // Reusing
}
}

Output:
Creating new Circle with color: Red
Creating new Circle with color: Green
Creating new Circle with color: Blue
Returning the already existing circle of color: Red

Common questions

Powered by AI

The Proxy and Flyweight design patterns both involve managing object interactions, but they differ in their use of shared instances. The Proxy pattern does not inherently rely on sharing instances; instead, it provides a surrogate or placeholder to control access to another object, focusing on access management. In contrast, the Flyweight pattern explicitly uses shared instances to minimize memory usage by managing a pool of objects that can be reused. Flyweight targets efficient memory use by reusing objects where possible, while Proxy focuses on control over direct object access .

Choosing between the Factory and Singleton design patterns depends on application requirements and system architecture. A Singleton pattern is appropriate when a class must only have one instance, such as configurations or logging, where a centralized control or access point is required. On the other hand, the Factory pattern is used when a system needs flexibility in the type of objects it creates, allowing variations in instantiated classes based on runtime logic, like GUI elements or document generation. The choice is influenced by design needs for extensibility (Factory) versus constraints on the number of allowable instances (Singleton).

The Flyweight design pattern reduces memory usage by sharing common parts of the state between multiple objects instead of storing them in each object individually. In the example provided, 'ShapeFactory' manages a pool of 'Circle' objects that are created or reused based on their color. When a 'Circle' of a specific color is requested, the factory checks if it already exists and returns the shared instance if it does. This approach minimizes memory by avoiding creating new objects for every request, thereby fitting more instances into the available RAM .

The Proxy design pattern is most beneficial in scenarios where direct access to an object should be controlled or delayed to enhance performance or security. It acts as a substitute or placeholder for another object, managing access to the actual object. For instance, by loading a heavy object such as an image only when it is required (lazy initialization), a proxy can reduce the initial load time and resource usage. It can also improve security by providing access control and logging capabilities before forwarding requests to the actual object .

The Factory design pattern leverages polymorphism by using a super class or an interface to define a common method (e.g., 'draw()' in the 'Shape' interface) that can be implemented by multiple subclasses (e.g., 'Rectangle' and 'Square'). This approach allows the Factory to create objects of different types, based on the specified subclass, while ensuring they can be accessed and used interchangeably through the same interface .

The Singleton design pattern ensures that a class has only one instance by making the class's constructor private and providing a static method that returns the instance of the class. This pattern solves two main problems: 1) it ensures that a class has just a single instance, preventing the creation of additional objects that could lead to inconsistencies; 2) it provides a global access point to that instance, simplifying access management across the system .

The Bridge design pattern separates abstraction from implementation by creating two separate class hierarchies: one for abstractions and another for implementations. The abstraction class (e.g., 'Shape') contains a reference to an object of the implementor interface (e.g., 'Color'), and these components can vary independently. This separation provides greater flexibility and scalability, allowing developers to alter or extend abstractions and implementations without affecting each other, thus facilitating code maintenance and evolution .

The primary pitfall of using the Singleton pattern in a multithreaded environment is the risk of creating multiple instances due to simultaneous access to the instance creation method. This can occur if two threads execute the 'getInstance()' method simultaneously when the Singleton instance is not initialized. To mitigate this, developers can implement a synchronized block within 'getInstance()' or use a volatile variable or the 'double-checked locking' technique to ensure visibility of changes across threads and minimize synchronization overhead .

Both the Bridge and Adapter design patterns deal with abstraction and interface issues, but their structural goals differ. The Bridge pattern focuses on separating abstraction from implementation, allowing the two to vary independently. It is used to decouple the interface and its implementation to enable flexibility and scalability. In contrast, the Adapter pattern is primarily about translating the interface of a class into another interface that a client expects. Unlike Bridge, it is not about extending functionality but making incompatible interfaces work together. Thus, while the Bridge pattern is aimed at planning and organizing code structure for future evolvability, the Adapter pattern is applied to solve immediate compatibility issues .

The Adapter design pattern plays the role of a bridge in software design, facilitating interaction between objects with incompatible interfaces. It achieves this by wrapping the incompatible object (Adaptee) with an Adapter class that implements the Target interface. The Adapter converts the calls to the methods of the Adaptee, allowing the client code (e.g., 'Railroad' interface) to operate seamlessly with different implementations without altering their structure .

You might also like