What are Design Patterns and Why Developers Should Know Them?

What are Design Patterns and Why Developers Should Know Them?

What is a Design Pattern?

When you look in Wikipedia for design patterns you will get a definition like this "a software design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design" But What they actually are? In simple terms, I would say Design Pattern is like a description of how to tackle the problems and design a solution. It’s not code - I repeat, ❌CODE

Using these patterns is considered good practice, as the design of the solution is quite tried and tested, resulting in higher readability of the final code. Design patterns are quite often created for and used by OOP Languages, like Java, in which most of the examples from here on will be written.

Types of Design Patterns

There are 26 Design Patterns classified into 3 Categories

  • Creational: These patterns are designed for class instantiation. They can be either class-creation patterns or object-creational patterns
  • Structural: These patterns are designed with regard to a class's structure and composition. The main goal of most of these patterns is to increase the functionality of the class(es) involved, without changing much of its composition
  • Behavioral: These patterns are designed depending on how one class communicates with others.

In this blog, we will go through one basic design pattern for each classified type.

Type 1 Creational - The Singleton Design Pattern

The Singleton Design Pattern is a Creational pattern, whose objective is to create only one instance of a class and to provide only one global access point to that object

example:

public class SingleTonClass{
    // initialize the instance as null.
    private static SingleTonClass instance = null;

    // private constructor, so it cannot be instantiated outside this class.
    private SingleTonClass() {  }

    // check if the instance is null, within a synchronized block. If so, create the object
    public static SingleTonClass getInstance() {
        if (instance == null) {
        synchronized (SingleTonClass.class) {
            if (instance == null) {
                instance = new SingleTonClass();
            }
        }
    }
    return instance;
}

Type 2 Structural - The Decorator Design Pattern

I will give you one small business scenario of when and where you should use this Pattern.

This Pattern's main Motto is "Software Entities or Classes Should be open for Extensions but closed for Modifications". Confused ?? read this line again after seeing the example below.

Say You opened a new IceCream Shop with a limited budget in the hand so you offered initially two flavors of IceCream say Vanilla and Chocolate and different costs for both of them. In your billing System, there is one class for different flavors of the IceCream Which Inherits the IceCream abstract class. After Somedays people began to come by in large numbers and enjoy the icecream you are serving. Then there came a new requirement like your customers are asking you for new flavors of the IceCream so you added a new flavor Say ButterScotch and inherited the IceCream abstract class Just as you did for Chocolate and Vanilla. Your problem solved your customer happy and you are happy. Then some new customers came by and started asking to add ons on the icecream. they want some dry fruits or chocolate syrup or some tutti frutti on top of the ice cream. You cannot say no to customers (The basic rule of any business). So Now What you will do ???

you cannot simply keep on adding the new classes based on the new requirements. that would be huge to maintain and it reduces the readability of your code and becomes more and more complex, when you face any issue you don't know which piece of code is the culprit behind that.

Solution: Here comes into the picture the decorator pattern which is widely used in the industry

So the typical class diagram of the decorator pattern looks like this.

decorator-class-diagram.png

Here Abstract Component or Abstract Class is nothing but our IceCream Abstract Class and the different Concrete Components are nothing but our classes for different flavors of the IceCream. and Abstract Decorator is the class that we will be using to decorate IceCream with the different AddOns. And the Concrete Decorators which are inheriting from the Abstract Decorator are nothing but our new AddOns Say Dryfruits and Chocolate Syrup.

Confused ?? So was I :)...

Let's get to coding to see this Pattern in Use

First We will make Abstract IceCream Class that all the different Flavors of the IceCream will inherit from:

public abstract class IceCream
{
    private String description;
    public IceCream(String description)
    {
        super();
        this.description = description;
    }
    public String getDescription()
    {
        return description;
    }
    public abstract double cost();
}

Then we will add our concrete IceCream flavor Classes

public class VanillaIceCream extends IceCream
{
    public VanillaIceCream()
    {
        super("Vanilla IceCream");
    }

    @Override
    public double cost()
    {
        return 100;
    }
}

public class ChocolateIceCream extends IceCream
{
    public ChocolateIceCream()
    {
        super("Chocolate IceCream");
    }

    @Override
    public double cost()
    {
        return 300;
    }
}

public class ButterScotchIceCream extends IceCream
{
    public ButterScotchIceCream()
    {
        super("ButterScotch IceCream");
    }

    @Override
    public double cost()
    {
        return 200;
    }
}

the AddOn Abstract Class also Inherits from the IceCream Abstract Class

public abstract class IceCreamAddOns extends IceCream
{
    protected IceCream iceCream;
    public IceCreamAddOns(String description, IceCream iceCre)
    {
        super(description);
        this.iceCream = iceCre;
    }
    public abstract String getDescription();
}

And Now the concrete implementation of this abstract class -> Our different AddOn Classes

public class ChocolateSyrup extends IceCreamAddOns
{
    public ChocolateSyrup(IceCream iceCream)
    {
        super("Chocolate Syrup", iceCream);
    }

    @Override
    public double cost()
    {
        return 80 + iceCream.cost();
    }

    @Override
    public String getDescription()
    {
        return iceCream.getDescription() + " With ChocoSyrup";
    }
}


public class DryFruits extends IceCreamAddOns
{
    public DryFruits(IceCream iceCream)
    {
        super("DryFruits", iceCream);
    }

    @Override
    public double cost()
    {
        return 100 + iceCream.cost();
    }

    @Override
    public String getDescription()
    {
        return iceCream.getDescription() + " With DryFruits";
    }

}

As you can see above, we can pass any subclass of IceCream to any SubClass of IceCreamAddOns, and get the added cost as well as the updated description. And since the AddOn Class is essentially of type IceCream, we can pass an AddOn into another AddOn. This Way, we can add any number of add-ons to a specific IceCream.

Now we will take some orders

public class OrderIceCream
{
    public static void main(String[] args)
    {
        IceCream iceCream = new VanillaIceCream();
        iceCream = new DryFruits(iceCream);
        iceCream = new ChocolateSyrup(iceCream);
        System.out.println("Your Order: " + iceCream.getDescription() + " and Your Bill: " + iceCream.cost());
    }
}

This prints the output as

Your Order: Vanilla IceCream With DryFruits With ChocoSyrup and Your Bill: 280.0

I know I know it's so much to take.....Now take a deep breath and will move to the third and the final category of this post

Type 3 Behavioral- The Command Design Pattern

definition for this pattern is The Command Pattern is a behavioral design pattern in which an object is used to represent and encapsulate all the information needed to call a method at a later time.

So what it actually means ?..... :)

The need for this pattern arose when requests needed to be sent without consciously knowing what you are asking for or who the receiver is

In this pattern, the invoking class is decoupled from the class that actually performs an action. The invoker class only has the callable method execute, which runs the necessary command, when the client requests it

Still a bit Confusing ???... Let's take a real-world scenario

You go to a restaurant and order(Command) some food to the Waiter(Invoker) Who then hands it over to the Chef (receiver) Who prepares your food. Now Let's code this using Command Design Pattern.

The Coding Flow goes Like this

command-class-diagram.png

So here you can give multiple Orders (Commands) to the Waiter (Invoker) he then hands it over to the Chef (receiver) to prepare your order. In Technical terms, you make a concrete command Which implements the Command Interface asking the receiver to complete the Action and send the Command to the Invoker. Here Invoker is the person who knows when to give this command and the Chef/receiver is the only one who knows what to do when given a specific command/order. So When the execute method of the invoker is run, it, in turn, causes the Command objects' execute method to run on the receiver thus completing necessary actions.

Ufff....... Never thought this much background process happens when you order food right ?? :)

So the coding goes like this:

Chef the Receiver

public class Chef
{
    public void cookBiryani()
    {
        System.out.println("Chef is cooking Spicy Chicken Biryani");
    }
    public void prepareLimeSoda()
    {
        System.out.println("Chef is Preparing Lime Soda");
    }
}

Command, the Interface

public interface Command
{
    public abstract void execute();
}

Order, the Concrete Method

public class Order implements Command
{
    private Chef chef;
    private String food;

    public Order(Chef chef, String food)
    {
        this.chef = chef;
        this.food = food;
    }

    @Override
    public void execute()
    {
        if (this.food.equalsIgnoreCase("Biryani"))
        {
            this.chef.cookBiryani();
        }
        else
        {
            this.chef.prepareLimeSoda();
        }
    }
}

Waiter, the Invoker

public class Waiter
{
    private Order order;

    public Waiter(Order newOrder)
    {
        this.order = newOrder;
    }

    public void execute()
    {
        this.order.execute();
    }
}

You, the Client

public class Client
{
    public static void main(String[] args)
    {
        Chef chef = new Chef();

        Order order = new Order(chef, "Biryani");
        Waiter waiter = new Waiter(order);
        waiter.execute();

        order = new Order(chef, "LimeSoda");
        waiter = new Waiter(order);
        waiter.execute();

    }
}

As you can see above the client makes an Order and sets the Receiver as the chef, The Order is sent to the Waiter(Invoker) who knows when to execute this order. So if the Invoker executed then the Orders execute method is run on the receiver i.e the Chef is given the command to either Cook Biryani or prepare Lime Soda.

Quick Recap

In this post, I have gone through the following:

  1. What is Design Pattern and Why it is important
  2. What are the different types of design patterns?
  3. One basic or common design pattern for each type

Thanks For Reading this..... :) I hope this post clarified some of your doubts on Design Patterns...

This is My First Blog Post and a Starting point for Many More to come ... I referred to Most of the concepts mentioned here from the FreeCodeCamp blogs Written By Sameeha Rahman. Thank You Sameeha for making Such an easy understandable content