Creational Design Patterns

September 30, 2022

Content

Introduction

Creational design patterns are the one type of design pattern that deals with object creation mechanisms, trying to create objects in a manner suitable to the situation, basically it describes how object is created. Creational design patterns are composed by two dominant ideas:

  1. Encapsulating the knowledge about which concrete classes the system uses.
  2. Hiding how the instance of these concrete classes are created and combined.

Creational are categorized as object-creational patterns and class-creational patterns, where object-creational is for creating the object, and class-creational is for class instantiation.

These are example of creational design patterns:

Builder

Builder design pattern is a part of Creational Design Pattern, which means it is responsible for the object creation. Builder design pattern separates the complexity of object creation so the same construction process can create multiple different object representation.

For example, there is a class User with property name, address, gopayWalletId, ovoWalletId, and danaWalletId. The all three wallets are optional, so during the registration, user can add all the wallet, one of three, two of three, or not add the wallet at all.

So for the instantiation it can be differ:

  • new User("name", "address")
  • new User("name", "address", "gopayWalletId")
  • new User("name", "address", "ovoWalletId", "danaWalletId")
  • new User("name", "address", "danaWalletId")
  • etc…

That’s need a lot of constructor. With builder design pattern it just need one, but can create multiple different object representation.

Factory Method

Factory method design pattern create a class that contain methods that create a class with specific value. For example:

class Processor {
	String manufacturer;
	String name;
  int baseSpeed;

  // All Arguments Constructor is defined.
}

// The `Processor` object can only be created using specific `manufacturer` value
// for example it's value can only be `intel` and `amd`
// for that we can implement the factory method to create Intel and AMD processor.

class ProcessorFactory {
	static Processor createIntelProcessor(String name, int baseSpeed) {
		return new Processor("Intel", name, baseSpeed);
  }

	static Processor createAmdProcessor(String name, int baseSpeed) {
		return new Processor("AMD", name, baseSpeed);
	}
}

// to create the Processor object, we can call the ProcessorFactory method
Processor ryzen5600g = ProcessorFactory.createAmdProcessor("Ryzen 5 5600G", 3900)

Abstract Factory

It is almost similar to factory method, for more detail we can extend the factory method example.

class Processor {
	String manufacturer;
	String name;
  int baseSpeed;

  // All Arguments Constructor is defined.
}

// The `Processor` object can only be created using specific `manufacturer` value
// for example it's value can only be `intel` and `amd`
// for that we can implement the factory method to create Intel and AMD processor.

class ProcessorFactory {
	static Processor createIntelProcessor(String name, int baseSpeed) {
		return new Processor("Intel", name, baseSpeed);
  }

	static Processor createAmdProcessor(String name, int baseSpeed) {
		return new Processor("AMD", name, baseSpeed);
	}
}

// to create the Processor object, we can call the ProcessorFactory method
Processor ryzen5600g = ProcessorFactory.createAmdProcessor("Ryzen 5 5600G", 3900);
class LaptopManufacturer {
	String brand;
	String country;
}

interface LaptopFactory {
	Proccesor createProcessor();
	LaptopManufacturer createManufacturer();
}

class LaptopForStaff implements LaptopFactory {
	Processor createProcessor() {
		return ProcessorFactory.createIntelProcessor("i5-11500H", 2900);
	}

	LaptopManufacturer createManufacturer() {
		return new LaptopManufacturer("Lenovo", "China");
	}
}

class LaptopForManager implements LaptopFactory {
	Processor createProcessor() {
		return ProcessorFactory.createIntelProcessor("i7-11850H", 2500);
	}

	LaptopManufacturer createManufacturer() {
		return new LaptopManufacturer("Lenovo", "China");
	}
}

class Laptop {
	Processor processor;
  LaptopManufacturer manufacturer;

	public Laptop(LaptopFactory laptopFactory) {
		this.processor = laptopFactory.createProcessor();
		this.manufacturer = laptopFactory.createManufacturer();
	}
}

new Laptop(new LaptopForStaff());
new Laptop(new LaptopForManager());

Prototype

Prototype design pattern is define that we can clone an existing object by creating a method for clone.

public class Level {
    private String difficulty;
    private String terrain;
    private int totalEnemy;

    public Level(String difficulty, String terrain, int totalEnemy) {
        this.difficulty = difficulty;
        this.terrain = terrain;
        this.totalEnemy = totalEnemy;
    }

    public Level clone() {
        return new Level(this.difficulty, this.terrain, this.totalEnemy);
    }

    // Getters and Setters are defined

}

Level levelOne = new Level("Easy", "Snow", 10);

Level levelTwo = levelOne.clone();
levelTwo.setTotalEnemy(15);
levelTwo.setTerrain("Desert");

Level levelThree = levelTwo.clone();
levelThree.setTotalEnemy(20);

Level levelFour = levelThree.clone();
levelFour.setDifficulty("Medium");

The example above is a simple case, imagine if there is a lot of property and the value are same as the previous instantiated object, the Prototype will be helpful.

Singleton

This design pattern defines that an object can only be instantiated once, but can be used multiple time. It is like a shared resource. Singleton also can be called as global object.

class LogManager {
  private static Logger logger;

	static Logger getLogger() {
		if (this.logger != null) {
			return this.logger;
		}

		return new Logger();
  }
}
// in javascript/typescript we can simplify it by creating global object
const logger: ILogger = {
  writeInfo: (message: String): void => {
    // ....
  },
  writeError: (message: String): void => {
    // ....
  },
  // ...
}
Object.freeze(logger)
export default logger

design-pattern