Essential Design Patterns in Java
Tags: java, designpatterns, programming, beginners
Published: 2023-01-26 09:53:30

Design patterns are reusable solutions to common software design problems. They provide a way to organize and structure code in a consistent and efficient manner. Some common design patterns include:

- The **Factory** 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.
- The **Abstract** Factory pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- The **Builder** pattern is a creational design pattern that separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
- The **Strategy** pattern is a behavioral design pattern that enables an algorithm's behavior to be selected at runtime.
- The **Decorator** pattern is a structural design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.
- The **Singleton** pattern is a creational design pattern that ensures that a class has only one instance, while also providing a global access point to this instance.
- The **Observer** pattern is a behavioral design pattern that allows an object (the subject) to notify other objects (the observers) when its state changes.

These patterns are useful because they provide a common language for developers and can make code more maintainable and understandable.

Below are 6 of the most commonly used design patterns with examples in java.

## Factory Pattern

![Factory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p2e07fzwm68xok233chh.jpg)

The factory pattern is a creational design pattern that provides a way to create objects without specifying the exact class of object that will be created. It allows a class to delegate the responsibility of creating objects to its sub-classes.

Here's an example of how the factory pattern can be implemented in Java:

```java
interface Shape {
    void draw();
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a square");
    }
}

class ShapeFactory {
    public Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}
```

In the above example, the _ShapeFactory_ class is the factory class that creates objects of different concrete classes (_Rectangle_ and _Square_) based on the input provided by the client. The client uses the factory class to create objects and doesn't need to know the specific class of object that will be created.

Here's an example of how the client code would use the factory pattern to create objects:

```java
ShapeFactory shapeFactory = new ShapeFactory();

//get an object of Rectangle and call its draw method.
Shape shape1 = shapeFactory.getShape("RECTANGLE");

//call draw method of Rectangle
shape1.draw();

//get an object of Square and call its draw method.
Shape shape2 = shapeFactory.getShape("SQUARE");

//call draw method of square
shape2.draw();

```

In this example, the client can create different shape objects by providing different inputs to the factory class, without needing to know the specific class of object that will be created. This allows for flexibility in the code and makes it easier to add new classes without modifying the existing code.

## Builder Pattern


![Builder Pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s85o7c5nd3mpdf96ma7s.jpg)

The builder pattern is a creational design pattern that allows for the construction of complex objects to be done step-by-step in a clear and organized manner. It separates the construction of an object from its representation, making it easier to change the internal representation of an object without affecting the client code.

Here's an example of how the builder pattern can be implemented in Java:

```java
class Computer {
    private String CPU;
    private String RAM;
    private String GPU;
    private String storage;

    private Computer(ComputerBuilder builder) {
        this.CPU = builder.CPU;
        this.RAM = builder.RAM;
        this.GPU = builder.GPU;
        this.storage = builder.storage;
    }

    public String getCPU() {
        return CPU;
    }

    public String getRAM() {
        return RAM;
    }

    public String getGPU() {
        return GPU;
    }

    public String getStorage() {
        return storage;
    }

    public static class ComputerBuilder {
        private String CPU;
        private String RAM;
        private String GPU;
        private String storage;

        public ComputerBuilder setCPU(String CPU) {
            this.CPU = CPU;
            return this;
        }

        public ComputerBuilder setRAM(String RAM) {
            this.RAM = RAM;
            return this;
        }

        public ComputerBuilder setGPU(String GPU) {
            this.GPU = GPU;
            return this;
        }

        public ComputerBuilder setStorage(String storage) {
            this.storage = storage;
            return this;
        }

        public Computer build() {
            return new Computer(this);
        }
    }
}

```
In this example, the _Computer_ class represents the complex object that is being built, and the _ComputerBuilder_ class is the builder class that constructs the _Computer_ object step-by-step. The _ComputerBuilder_ class has setter methods for each of the fields in the _Computer_ class, and a _build()_ method that returns the constructed _Computer_ object.

Here's an example of how the client code would use the builder pattern to create a _Computer_ object:

```java
Computer computer = new Computer.ComputerBuilder()
                .setCPU("i7")
                .setRAM("16GB")
                .setGPU("GTX 1080")
                .setStorage("1TB")
                .build();

```
In this example, the client can create a _Computer_ object by providing different values for each of the fields, in a step-by-step manner, using the builder class. This makes the code more readable and easy to understand, as the client does not have to worry about the internal representation of the _Computer_ object.

## Strategy Design Pattern

The strategy pattern is a behavioral design pattern that allows for the selection of an algorithm at runtime. It defines a family of algorithms, encapsulates each one, and makes them interchangeable. This allows the algorithm to vary independently from the clients that use it.

Here's an example of how the strategy pattern can be implemented in Java:

```java
interface PaymentStrategy {
    void pay(int amount);
}

class CreditCardStrategy implements PaymentStrategy {
    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

    public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate){
        this.name=nm;
        this.cardNumber=ccNum;
        this.cvv=cvv;
        this.dateOfExpiry=expiryDate;
    }
    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }
}

class PayPalStrategy implements PaymentStrategy {
    private String emailId;
    private String password;

    public PayPalStrategy(String email, String pwd){
        this.emailId=email;
        this.password=pwd;
    }
    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using PayPal.");
    }
}

class ShoppingCart {
    List<Item> items;
    PaymentStrategy paymentStrategy;

    public ShoppingCart(){
        this.items=new ArrayList<Item>();
    }

    public void addItem(Item item){
        this.items.add(item);
    }

    public void removeItem(Item item){
        this.items.remove(item);
    }

    public int calculateTotal(){
        int sum = 0;
        for(Item item : items){
            sum += item.getPrice();
        }
        return sum;
    }

    public void pay(){
        int amount = calculateTotal();
        paymentStrategy.pay(amount);
    }

    public void setPaymentStrategy(PaymentStrategy paymentMethod){
        this.paymentStrategy=paymentMethod;
    }
}

```
In this example, the _PaymentStrategy_ interface defines the pay method that is used to make a payment. The _CreditCardStrategy_ and _PayPalStrategy_ classes are concrete implementations of this interface that provide different ways to make a payment (i.e. using credit/debit card or PayPal). The _ShoppingCart_ class is the client that uses the _PaymentStrategy_ interface to make a payment. The _ShoppingCart_ class can set the payment strategy at runtime using the _setPaymentStrategy_ method.

Here's an example of how the client code would use the strategy pattern:

```java
ShoppingCart cart = new ShoppingCart();
cart.addItem(new Item("item1", 100));
cart.addItem(new Item("item2", 50));

// Selecting the CreditCardStrategy
cart.setPaymentStrategy(new CreditCardStrategy("John Doe","1234567890123456", "123", "12/2022"));
cart.pay();

// Selecting the PayPalStrategy
cart.setPaymentStrategy(new PayPalStrategy("test@example.com", "password"));
cart.pay();

```
In this example, the client creates an instance of the _ShoppingCart_ class and adds some items to it. Then, the client selects the payment strategy by setting it on the _ShoppingCart_ object using the _setPaymentStrategy_ method. This can be done at runtime, so the client can switch between different payment strategies as needed. Finally, the client calls the pay() method on the _ShoppingCart_ object to make the payment using the selected strategy.

In this example the client code can switch between credit card and PayPal payment methods, but it could also include other types of payment methods such as bank transfer, etc. With this pattern the client code does not need to worry about the details of each payment method, it only needs to know about the common _PaymentStrategy_ interface and that the concrete classes that implement it are able to pay.


## Decorator Pattern

![Decorator Pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23dyxxi1aosmta6lvj95.jpg)

The decorator pattern is a structural design pattern that allows for the dynamic addition of behavior to an individual object, either by wrapping it with a decorator object or by extending it with additional functionality. The decorator pattern allows for the creation of flexible and reusable code.

Here's an example of how the decorator pattern can be implemented in Java:

```java
interface Shape {
    void draw();
}

class Circle implements Shape {
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;

    public ShapeDecorator(Shape decoratedShape){
        this.decoratedShape = decoratedShape;
    }

    public void draw(){
        decoratedShape.draw();
    }
}

class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }

    @Override
    public void draw() {
        decoratedShape.draw();
        setRedBorder(decoratedShape);
    }

    private void setRedBorder(Shape decoratedShape){
        System.out.println("Border Color: Red");
    }
}

```
In this example, the _Shape_ interface defines the _draw()_ method that is used to draw a shape. The _Circle_ class is a concrete implementation of this interface that provides the behavior for drawing a circle. The _ShapeDecorator_ is an abstract class that also implements the Shape interface, but it has an additional _decoratedShape_ attribute that references the shape it is decorating. The _RedShapeDecorator_ class is a concrete decorator that adds a red border to the shape it decorates.

Here's an example of how the client code would use the decorator pattern:

```java
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());

circle.draw();
redCircle.draw();

```
In this example, the client creates an instance of the _Circle_ class, and then creates an instance of the _RedShapeDecorator_ class, passing the _Circle_ instance as an argument. The _RedShapeDecorator_ class wraps the _Circle_ instance and adds the additional behavior of drawing a red border. The client can then call the _draw()_ method on both the _circle_ and _redCircle_ objects, and the behavior of the _Circle_ class will be decorated with the additional behavior provided by the _RedShapeDecorator_ class.

In this example, we used the decorator pattern to add the red border to the shape , but you can use this pattern to add or modify other features or behavior to the decorated object. The key of this pattern is that the client does not need to know about the decorator classes, it only needs to know about the interface of the decorated object.

## Singleton Pattern

The singleton pattern is a creational design pattern that ensures that a class has only one instance, while also providing a global access point to this instance. The singleton pattern is used in situations where a single instance of a class must control the action throughout the execution.

Here's an example of how the singleton pattern can be implemented in Java:

```java
class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public void doSomething() {
        // some code
    }
}

```
In this example, the _Singleton_ class has a private constructor, which means that the class cannot be instantiated from outside the class. The class also has a static instance attribute, which will hold the single instance of the class. The _getInstance()_ method is used to get the single instance of the class, and it uses lazy initialization to create the instance only when it is first needed.

Here's an example of how the client code would use the singleton pattern:

```java
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();

System.out.println(singleton1 == singleton2); // true

```
In this example, the client code calls the _getInstance()_ method twice, but it only gets one instance of the _Singleton_ class. The == operator is used to compare the references of the two instances, and it returns true because they are the same instance.

The singleton pattern is useful when you want to make sure that a class is instantiated only once and that there is a single instance of it that is globally accessible. It is also useful when you want to control the resources or the number of instances of a class and you want to enforce a single point of control over it.

It is important to note that the singleton pattern is not thread-safe by default. If multiple threads access the _getInstance()_ method simultaneously, multiple instances of the Singleton class may be created. To make the singleton pattern thread-safe, you can use the synchronized keyword on the _getInstance_ method or you can use the double-checked locking pattern.


## Observer Pattern

![Observer Pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b0yandmud4635hurgro2.jpg)
The observer pattern is a behavioral design pattern that allows an object (the subject) to notify other objects (the observers) when its state changes. The observer pattern is used in situations where one object needs to be informed of changes to the state of another object, without the two objects having a direct reference to each other.

Here's an example of how the observer pattern can be implemented in Java:

```java
interface Observer {
    void update(String message);
}

class ConcreteObserver implements Observer {
    public void update(String message) {
        System.out.println("Received message: " + message);
    }
}

interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String message;

    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }

    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }
}

```
In this example, the _Observer_ interface defines the _update()_ method that will be called when the state of the subject changes. The _ConcreteObserver_ class is a concrete implementation of the Observer interface that provides the behavior for handling the update.

The _Subject_ interface defines the methods for registering and removing observers, as well as for notifying all registered observers when the state of the subject changes. The _ConcreteSubject_ class is a concrete implementation of the Subject interface that maintains a list of observers and provides the methods for registering and removing observers, as well as for notifying all registered observers when the state of the subject changes.

Here's an example of how the client code would use the observer pattern:

```java
ConcreteSubject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();

subject.registerObserver(observer);
subject.setMessage("Hello World!");

```
In this example, the client code creates an instance of the _ConcreteSubject_ class and an instance of the _ConcreteObserver_ class. The observer is then registered with the subject using the _registerObserver()_ method. The client then sets the message of the subject using the _setMessage()_

In conclusion, Design patterns are an essential part of software development, they help developers to solve common problems and make the code more maintainable, reusable and scalable. Understanding and using design patterns can help developers to write better and more efficient code. It is important to note that design patterns are not a one-size-fits-all solution, and it's essential to understand the problem and context before applying a specific pattern.

---
_This article was created with the help of AI_