(Creational)Design Patterns Tutorial Part-2
In this tutorial, we will look into Creational Design Patterns. If you miss out introduction then first check out this
In software engineering, creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation.
Source: wikipedia.org
~Singleton Design Pattern
Singleton Design Pattern ensures that only Object of a particular class is created. Example Problem: If you want to use Database class in multiple places, then every time you have to create its objects to access it. Singleton came here as a rescue, it will assure only one instance of Database class is created and return the same instance every time.
Example: In Kotlin we can simply use ‘object’ to make a class Singleton.
object Database{
init {
println("Initializing with object: $this")
}
fun print() = println("Printing with object: $this")
}//Usage: Database.print()
Output: Initializing with object: @2f0e140b
Printing with object: @2f0e140b
Java: Note: synchronized keyword used for thread safety
class Database {
private static Database dbObject;
private Database()
{
System.out.println("Initializing with object");
}
public static synchronized Database getInstance()
{
// create object if it's not already created
if (dbObject == null) {
dbObject = new Database ();
}
// returns the singleton object
return dbObject;
}
public void print()
{
System.out.println("Printing with object")
}
}
~Factory Method/Factory Pattern
Factory Method or Factory Pattern both meant the same. It is a widely used design pattern for object creation.
Each object is created through a factory method — which can be an Interface or Abstract class.
“defines an interface for creating an object, but lets subclasses decide which class to instantiate.”
The client can create an object through these common interface/abstract class to simplifies the process.
Provide Loose Coupling- Easy Maintianence- Upgrade- **Object creation logic is hidden from the client.
Confused? No Problem. We can clear our doubts by an example
Example Problem: Suppose you want to build your own car, that consist of Engine and Type
Now your client wants some modification instead of SportCar and DieselEngine they want a car with PetrolEngine and FamilyCar. So you have two option in this scenario. (1) Create another method( so for every change you have to create a new method) (2)Change the existing method(so every change you have rewrite it)
Solution: To solve this problem we will create a Factory of Cars and leaves the Type & Engine to define in a subclass.
First Look into this simple Factory Pattern implementation: Here we created a Common Interface ‘Animal’ implemented by different Animals class like Dog, Cat. Then we have created an AnimalFactory to get the desired Animal Object and print eat() method.
**sub-classes can’t be reached without using their respective factory. This way, their creation is both hidden from the client and is dependent on the factory.
//Define a common interface for creating an Object
public interface Animal {
void eat();
}public class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog is eating, woof!");
}
}
public class Cat implements Animal {
@Override
public void eat() {
System.out.println("Cat is eating, meow!");
}
}
public class Rabbit implements Animal {
@Override
public void eat() {
System.out.println("Rabbit is eating, squeak!");
}
}//we have a group of classes, we can designate a factory for thempublic class AnimalFactory {
//lets subclasses decide which class to instantiate.
public Animal getAnimal(String animal) {
if (animal.equals(null)) return null;
if (animal.equalsIgnoreCase("Dog")) {
return new Dog();
} else if (animal.equalsIgnoreCase("Cat")) {
return new Cat();
} else if (animal.equalsIgnoreCase("Rabbit")) {
return new Rabbit();
}
return null;
}
}
Now Call using Factory Method
/*This way, we have a factory to instantiate our objects in a predefined way by the factory, without direct contact with the objects themselves.*/
public class Main {
public static void main(String[] args) {
AnimalFactory animalFactory = new AnimalFactory();
Animal animal = animalFactory.getAnimal("dOg");
animal.eat();
Animal animal2 = animalFactory.getAnimal("CAT");
animal2.eat();
Animal animal3 = animalFactory.getAnimal("raBbIt");
animal3.eat();
}
}
Now Come to Cars Solution:
//Define a Common Interface
public interface Car {
void setEngine(Engine engine);
void setType(Type type);
}
public class Engine {
private String model;
public Engine(String model) {
this.model = model;
}
// Getters and Setters
}
public class Type {
private String model;
public Type(String model) {
this.model = model;
}
// Getters and Setters
}
public class SportCar implements Car {
private Engine engine;
private Type type;
public SportCar(Engine engine, Type type) {
this.engine = engine;
System.out.println("Sport Car Diesel Engine");
this.type = type;
System.out.println("Sport Car");
}
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public void setType(Type type) {
this.type = type;
}
}
public class FamilyCar implements Car {
private Engine engine;
private Type type;
public FamilyCar(Engine engine, Type type) {
this.engine = engine;
System.out.println("Family Car Petrol Engine");
this.type = type;
System.out.println("Family Car");
}
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public void setType(Type type) {
this.type = type;
}
}
public class CarsFactory {
public Car getCar(Engine engine, Type type) {
if (engine.getModel().equals("Diesel")) {
return new SportCar(engine, type);
} else if (engine.getModel().equals("Petrol")) {
return new FamilyCar(engine, type);
} else {
System.out.println("Incompatible models of engine and car type.");
}
return null;
}
}//Used for Creating Different Cars Object CarsFactory factory = new CarsFactory();
Engine engineSport = new Engine("Diesel");
Type type1 = new Type("Sport");
Engine engineFamily = new Engine("Petrol");
Type type2 = new Type("Family");
Car carSport = factory.getCar(engineSport, type1);
Car carFamily = factory.getCar(engineFamily, type2);
// Car carHybrid = factory.getCar(engineSport, type2);
Look if we to add a new Factory for every Car like SportCar, FamilyCar. So is there any HybridCar we have to add a factory method that will return Hybrid Car using a new Keyword?
Having multiple factories would end up in multiple new
keywords - which is against what the Factory Method stands for.
The Fix to this is ‘ Abstract Factory Design Pattern’
~Abstract Factory Pattern
The book Design Patterns: Elements of Reusable Object-Oriented Software states that an Abstract Factory “provides an interface for creating families of related or dependent objects without specifying their concrete classes”
**In Simple words, provides the practice of creating a Factory of Factories. It is built upon the Factory Pattern.
This pattern is responsible for creating all other factories as its sub-classes, exactly like how factories are responsible for creating all of their own sub-classes.
Example based on previous Animal example:
// create a common interface like Factory Pattern for creating an Object. assume it Factory Pattern 1
interface Pet {
void eat();
}
public class Dog implements Pet {
@Override
public void eat() {
System.out.println("Dog eats bow bow");
}
}
class Cat implements Pet {
@Override
public void eat() {
System.out.println("Cat eats meow meow");
}
}
// create a common interface like Factory Pattern for creating an Object. assume it Factory Pattern 2
interface Human {
void feedPet();
}
class Adult implements Human {
@Override
public void feedPet() {
System.out.println("Adult Feed Pets");
}
}
class Child implements Human {
@Override
public void feedPet() {
System.out.println("Child Feed Pets");
}
}//AbstractFactory's concern is the ability to provide these objects //to the FactoryProducer, not to instantiate themabstract class AbstractFactory {
public abstract Pet getPet(String petName);
public abstract Human getHuman(String humanName);
}
class HumanFactory extends AbstractFactory {
//lets subclasses decide which class to instantiate. like Factory //Pattern. Factory Pattern for 1
@Override
public Human getHuman(String humanName) {
if (humanName.equalsIgnoreCase("Child")) {
return new Child();
} else if (humanName.equalsIgnoreCase("Adult")) {
return new Adult();
}
return null;
}
@Override
public Pet getPet(String petName) {
// if (petName.equals("Dog")) {
// return new Dog();
// } else if (petName.equals("Cat")) {
// return new Cat();
// }
return null;
}
}
class PetFactory extends AbstractFactory {
//lets subclasses decide which class to instantiate. like Factory //Pattern. Factory Pattern for 2
@Override
public Human getHuman(String humanName) {
return null;
}
@Override
public Pet getPet(String petName) {
if (petName.equalsIgnoreCase("Dog")) {
return new Dog();
} else if (petName.equalsIgnoreCase("Cat")) {
return new Cat();
}
return null;
}
}//create the FactoryProducer which is charged with the //responsibility to instantiate the adequate factories, with the //help of the AbstractFactory
//So we can say now factory of factories class FactoryProducer{
public static AbstractFactory getFactory(String factoryName){
if (factoryName.equalsIgnoreCase("Pet")) {
return new PetFactory();
} else if (factoryName.equalsIgnoreCase("Human")) {
return new HumanFactory();
}
return null;
}
}
class Main{
public static void main(String[] args){
AbstractFactory humanFactory = FactoryProducer.getFactory("Human");
AbstractFactory petFactory = FactoryProducer.getFactory("Pet");
Human human = humanFactory.getHuman("Child");
Pet pet = petFactory.getPet("Dog");
human.feedPet();
pet.eat();
}
}
Result:
Child Feed Pets
Dog eats bow bow
~Builder Design Pattern
This Pattern will make your hectic day happy. But how?
Assume you are working with a data class to get and set the values, you are setting the values through the constructor and the number of fields is too many. Each time to set values you have to pass the value of every field whether it is required or not. A problem like below:
public class Person {
private String name;
private String gender;
private String status;
private String height;
private String age;
private String city;
private int state;
private int country;
public Person(String name, String gender, String status, String height, String age, String city, int state, int country) {
this.name = name;
this.gender = gender;
this.status = status;
this.height = height;
this.age = age;
this.city = city;
this.state = state;
this.country = country;
}
//getters and setters
}
It looks so weird and every time you have to pass each value to the constructor. Here comes the Builder Design Pattern to save us.
Using Builder pattern is very good when dealing with complex objects(Hard to read & maintain). It helps in building the object step by step.
Let's learn from the Solution to the above Problem:
public class PersonData {
private String name;
private String gender;
private boolean status;
private String state;
private String country;
// create getters to get values
public String getName() {
return name;
}
public String getCountry() {
return country;
}
//step 1: make the constructor private,
// Constructor is private because now only way to intialize is through Builder
private PersonData(Builder builder){
this.name = builder.name;
this.gender = builder.gender;
this.status = builder.status;
}
public static class Builder{
//Copy all the fields into static Builder Class
private String name;
private String gender;
private boolean status;
private String state;
private String country;
//if we want few fields mandatory, we can create a public constructor with those fields
Builder(String state, String country){
this.state = state;
this.country = country;
}
//Now we create methods to set values to above field one by one
//each time we will return Builder itself. So that it can be update with new required fields
public Builder withName(String name){
this.name =name;
return this;
}
public Builder withGender(String gender){
this.gender =gender;
return this;
}
public Builder isStatus(boolean status){
this.status =status;
return this;
}
//we can create methods for other fields like above
public PersonData build(){
return new PersonData(this);
}
}
}
class Main{
public static void main(String[] args){
PersonData data = new PersonData.Builder("Haryana","Delhi")
.withName("Manish")
.withGender("Male")
.isStatus(true)
.build();
//Reusing a Builder
//We can reuse a builder as per our needs
PersonData.Builder builder = new PersonData.Builder("Haryana","Delhi");
data = builder.withName("Manish")
.withGender("Male")
.isStatus(true).build();
//lets say we only want a person with few data
PersonData data2 = new PersonData.Builder("Haryana","Delhi")
.withName("Manish")
.build();
System.out.println(data.getName());
System.out.println(data.getCountry());
}
}
Note**: In Kotlin there is no need to use Builder Pattern because kotlin provides a feature of default parameters, which allows you to define default values for each optional constructor argument.
Dependency Injection Pattern
For example, To make Tea you need Milk and Water. So Tea Class is dependent on the Milk and Water class. In the general case, we hard-coded all the dependency needed for Tea inside the Tea class itself. So in the future, Tea is required more dependency than we have to add new dependencies and do update & code changes at many classes.
So, in the Dependency Injection pattern, we provide the dependency of a class from outside the class and no dependency will be provided in the same class.
Problem:
- Difficult to test
- If dependency changes like Water & Milk, we have to do changes on dependent class i.e Tea
class Tea{
Milk milk = new Milk();//Dependent on Milk
Water water = new Water();//Dependent on Water
void makeTea(){
//Do something with milk and water
}
}
Solution: Provide Dependency from outside the class
class Tea(Milk milk , Water water){
Milk milk = milk;//Dependent on Milk
Water water = water;//Dependent on Water
void makeTea(){
//Do something with milk and water
}
}//Call will be like
Milk milk = new Milk();
Water water = new Water();
Tea tea = new Tea(milk , water);
tea.makeTea();
We can use some framework like Hilt Dagger, that will do work you behind the scene.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
I hope you have learned something from this tutorial.
Connect with me on Linkedin