Designing with Patterns: A Comparative Analysis of Factory, Builder, and Prototype

Design patterns are a set of best practices and solutions to common software development problems. They provide a way for developers to solve recurring problems in a consistent and efficient manner. Among the most widely used design patterns are the Factory, Builder and Prototype patterns. These three patterns are used for object creation and encapsulation, but have distinct differences in terms of their implementation and ideology. In this article, we will take a sample example and convert the same code to Factory, Builder and Prototype pattern. We will also explain how these three patterns approach the same problem differently.

Sample Example

Problem

Consider a scenario where we need to create a class that represents a computer configuration. The class should have properties such as CPU, RAM, and storage. The class should also have methods to get and set these properties.

Code Example

class Computer {
    private String CPU;
    private int RAM;
    private int storage;

    public Computer(String CPU, int RAM, int storage) {
        this.CPU = CPU;
        this.RAM = RAM;
        this.storage = storage;
    }

    public String getCPU() {
        return CPU;
    }

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

    public int getRAM() {
        return RAM;
    }

    public void setRAM(int RAM) {
        this.RAM = RAM;
    }

    public int getStorage() {
        return storage;
    }

    public void setStorage(int storage) {
        this.storage = storage;
    }
}

This code example shows a basic implementation of the Computer class with the properties and methods required to get and set the properties.

Factory Pattern

Overview

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. The Factory pattern uses a factory method, which is a method that creates objects, to create objects of a superclass. The factory method is defined in an interface and implemented by concrete classes. This pattern allows the creation of objects to be encapsulated and makes the code more flexible, as the class of objects that is created can be changed without affecting the code that uses them.

Code Example

interface ComputerFactory {
    public Computer createComputer();
}

class DesktopComputerFactory implements ComputerFactory {
    @Override
    public Computer createComputer() {
        return new Computer("i7", 16, 1000);
    }
}

class LaptopComputerFactory implements ComputerFactory {
    @Override
    public Computer createComputer() {
        return new Computer("i5", 8, 500);
    }
}

In this example, we have an interface ComputerFactory that defines the factory method createComputer(). The DesktopComputerFactory and LaptopComputerFactory classes implement the ComputerFactory interface and provide concrete implementations of the createComputer() method. The createComputer() method returns the corresponding Computer object with the properties set as per the factory (Desktop or Laptop in this case).

Builder Pattern

Overview

The Builder pattern is a creational design pattern that separates the construction of an object from its representation. The Builder pattern uses a builder class, which is a separate class that is responsible for creating an object, to create an object. The builder class has methods for setting the properties of the object and a method for returning the object. This pattern allows the creation of complex objects to be encapsulated and makes the code more flexible, as the class of objects that is created can be changed without affecting the code that uses them.

Code Example

class Computer {
    private String CPU;
    private int RAM;
    private int storage;

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

    public String getCPU() {
        return CPU;
    }

    public int getRAM() {
        return RAM;
    }

    public int getStorage() {
        return storage;
    }

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

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

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

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

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

In this example, we have a Computer class that has private properties for CPU, RAM, and storage. The Computer class also has a ComputerBuilder inner class that has methods for setting the properties of the Computer object. The Computer class has a private constructor that takes a ComputerBuilder object and sets the properties of the Computer object. The ComputerBuilder class also has a build() method that returns a new Computer object with the properties set by the ComputerBuilder class.

Prototype Pattern

Overview

The Prototype pattern is a creational design pattern that allows objects to be created by copying existing objects. The Prototype pattern uses a prototype class, which is a class that has a method for creating a copy of the object. This pattern allows the creation of complex objects to be encapsulated and makes the code more flexible, as the class of objects that is created can be changed without affecting the code that uses them.

Code Example

class Computer implements Cloneable {
    private String CPU;
    private int RAM;
    private int storage;

    public Computer(String CPU, int RAM, int storage) {
        this.CPU = CPU;
        this.RAM = RAM;
        this.storage = storage;
    }

    @Override
    public Computer clone() {
        try {
            return (Computer) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public String getCPU() {
        return CPU;
    }

    public int getRAM() {
        return RAM;
    }

    public int getStorage() {
        return storage;
    }
}

In this example, we have a Computer class that implements the Cloneable interface. The Computer class has a clone() method that creates a copy of the Computer object. The properties of the new object can be modified after being cloned.

Comparison

Ideology

The Factory pattern is based on the idea of creating objects without specifying the exact class of object that will be created. The Builder pattern is based on the idea of separating the construction of an object from its representation. While the Prototype pattern is based on the idea of creating objects by copying existing objects.

Object creation process

The Factory pattern uses a factory method to create objects, the Builder pattern uses a separate builder class to create objects, and the Prototype pattern uses a clone method to create objects.

Flexibility

All three patterns offer flexibility in terms of being able to change the class of objects that are created without affecting the code that uses them. However, the Builder pattern offers more flexibility in terms of setting properties, while the Prototype pattern offers more flexibility in terms of creating multiple objects with different properties.

Use cases

The Factory pattern is appropriate for situations where the objects being created have a simple structure and the class of objects can be changed easily. The Builder pattern is appropriate for situations where the objects being created have many properties and the creation process needs to be encapsulated. The Prototype pattern is appropriate for situations where many copies of an object need to be created with different properties.

Conclusion

The Factory, Builder, and Prototype patterns are all creational design patterns that provide solutions for object creation. They all have their own strengths and are appropriate for different use cases. Understanding how to use these patterns correctly is essential for any software developer. The Factory pattern is appropriate for simple object creation, the Builder pattern is appropriate for complex object creation, and the Prototype pattern is appropriate for creating multiple objects with different properties. It’s important to choose the right pattern for the situation and use them correctly to avoid making the code more complex and harder to maintain.

For more post like this; you may also follow this profile – https://dev.to/asifbuetcse

Leave a Comment