Factory Method In Brief: Design Pattern
Tags: design, architecture, beginners, programming
Published: 2022-10-13 08:26:58

This pattern is a Creational Design Pattern that provides an interface or abstract for creating the type of object in a superclass but allows the subclass to change the type of object that will be created.

It suggests that developers should replace object creation using
the `constructor` or using the `new` operator. This sounds strange but an object will be created using only the `constructor` that is called inside a **factory method**.

This may look complex at first glance but believe me, it promotes loose coupling and code reuse.

**Let's understand with an Example**

## Problem Statement

Imagine you want to add different logging behavior for different environments of applications i.e `Production`, `Development`, `Test`, and `Debug`.

- In **`Production`**: Only `loglevel=error|warn` is displayed.
- In **`Development`**: All `loglevel=info|error|warn|debug` is displayed.
- In **`Debug`**: Only `loglevel=debug` must be displayed.

> For simplicity let's only work with two environments i.e `Production` and `Development`.

## Implementation without using a pattern

```
// Concrete implementation

class Logger is
    env: string

    public method LogError(message: string): void is
	    print message

	public method LogInfo(message: string): void is
		if env = ("Development" or "Test" or "Debug") then
			print message

	public method LogWarning(message: string): void is
		print message

	public method LogDebug(message: string): void is
		if env = ("Development" or "Test" or "Debug") then
			print message
```

```
// Implementation of Logger
function DoSomeWork(): void is
	...
	logger = new Logger()
	...
	var condition = isValid();

	if condition is true then
		logger.LogInfo("Some work is completed");
		return
	...
	logger.LogError("Unable to complete work")
```

Implementation of `Logger` involves so much of `if-else` or '`if` (the same thing can be performed using the `switch`).

**Imagine in the future**

- Some new environment is added let's say `Cloud`. So it brings new problems and conditions with it.
  - Making developer change implementation of class `Logger`.
  - Updating condition according to it
- Business rules changed for logger
  - So again developer has to change the internal of `Logger`

> **Thus, this implementation is not maintainable and prone to unwanted behavior to occur.**

## Implementing Factory Method

The developer creates an abstract or interface called `Logger` and provides the concrete implementation for various subclasses of the common superclass `Logger` for various environments i.e `ProductionLogger`, `DevelopmentLogger`, `TestingLogger` and `DebugLogger`.

So each method of the subclass of `Logger` must contain its implementation and business rules.

I know it's hard to grasp to understand because adds up complexity.

![UML Diagram For Logger(Superclass) and Concrete Implementation(Subclass). Diagrams show Is-A relation between abstract class and concrete class](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q62ad123ze0f6t3twgi2.jpg)

_For those who don't understand the UML diagram, it shows the IS-A relationship between the abstract `Logger` class and subclasses (`DevelopmentLogger`, `TestingLogger` etc) such that `DevelopmentLogger` is a `Logger`._

The developer creates an `interface` named `ILoggerBuilder`. That is responsible to provide an object of type `Logger` according to the requirement or some logic. the UML diagram shows the IS-A relationship between the abstract `Logger` class and subclasses (`DevelopmentLogger`, `TestingLogger` etc) such that `DevelopmentLogger` is a `Logger`.

The developer creates an `interface` named `ILoggerFactory`. That is responsible to provide an object of type `Logger` according to the requirement or some logic.

`EnvironmentLoggerFactory` implements `ILoggerFactory` that is responsible for creating an appropriate object of Subclass of `Logger` according to the environment on which the application is executing or say **there is complex logic for building some object**;

![UML diagram for interface ILoggerFactory and Concrete Implementation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5v4o8gtyhyskfyemkofu.jpg)

**Let's write some pseudo-code**

```
abstract class Logger is
    public abstract method LogInfo(message: string): void

    public abstract method LogError(message: string): void

    public abstract method LogDebug(message: string): void

    public abstract method LogWarn(message: string): void
```

```
class ProductionLogger inherits Logger is
    public method LogInfo(message: string): void is
      // Does Nothing
      return

    public method LogError(message: string): void is
    	print message

    public method LogWarn(message: string): void is
    	print message

    public method LogDebug(message: string): void is
    	// Does Nothing
    	return
```

```
class DevelopmentLogger inherits Logger is
    public method LogInfo(message: string): void is
      print message

    public method LogError(message: string): void is
    	print message

    public method LogWarn(message: string): void is
    	print message

    public method LogDebug(message: string): void is
    	return
```

```
class TestingLogger inherits Logger is
    public method LogInfo(message: string): void is
      // Does Nothing
      return

    public method LogError(message: string): void is
    	print message

    public method LogWarn(message: string): void is
    	print message

    public method LogDebug(message: string): void is
    	// Does Nothing
    	return
```

```
class DebugLogger inherits Logger is
public method LogInfo(message: string): void is
// Does Nothing
return

    public method LogError(message: string): void is
    	// Does Nothing
    	return

    public method LogWarn(message: string): void is
    	// Does Nothing
    	return

    public method LogDebug(message: string): void is
    	print message
```

Now developer adds `EnvironmentLoggerFactory` that implements `ILoggerFactory`

```
interface ILoggerFactory is
  CreateLogger(): Logger
```

```
class EnvironmentLoggerFactory implements ILoggerFactory
  public CreateLogger(): Logger is
    env = getCurrentEnv()

    if env
      is "Production" then return new ProductionLogger()
      is "Development" then return new DevelopmentLogger()
      is "Testing" then return new TestingLogger()
      is "Debug" then return new DebugLogger()

    throw new Exception("No logger attached to current environment")
```
![UML diagram of entire structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6eanpfv4aiqo64hirehg.jpg)

**Implementation of Logger**

```
function DoSomeWork(): void is
	...
	logger = new EnvironmentLoggerFactory().CreateLogger()
	...
	var condition = isValid();

	if condition is true then
		logger.LogInfo("Some work is completed");
		return
	...
	logger.LogError("Unable to complete work")
```

**Again imagine the future**

- Some new environment is added let's say `Cloud`.
  - **SOLUTION**: Now the developer has to develop a new subclass `CloudLogger` that inherits `Logger`.
  - Rules can be implemented without thinking that they will break in the development or production environment.
- Business rules changed for the logger, for example, all the logs in the cloud must be stored in a database
  - **SOLUTION**: Just implement a concrete class `CloudDatabaseLogger` that inherits `Logger`.

## Applicability

- **When developers do not know the exact type of objects that code should work with**
  - Factory method separates object creation from the code that uses it
  - It provides an ease to extending the construction of an object
- **When the developer wants to save resources by reusing existing objects rather than by rebuilding them**
  - This can be achieved using caching or memoization


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o20h1y5uwzbddnr7jmkt.jpg)


## Generic Implementation of Pattern

1. All the products must be must inherit or implement` Product`
2. Add a factory method inside `CreatorFactory` that always returns an object of base type `Product`
3. Replace all constructor calls in the code with the factory method.

> Developer can pass argument inside this factory method to control object creation behavior

## Advantages

1. Avoid tight coupling
2. _Single Responsibility Principal_, thus all object creation code is separated, allowing code management easy.
3. _Open/Close Principal_, the developer can introduce a new product according to changes without breaking client code.


## Conclusion 
The factory method is a powerful way to create complex objects it promotes loose coupling, the single responsibility principle, and the open/close principle.

Follow me on [GitHub](https://github.com/harshrastogiexe)

To know more about this pattern, check this video by **Christopher Okhravi**
{% embed https://www.youtube.com/watch?v=EcFVTgRHJLM&t=757s %}


### References 
- [Design Patterns: Elements of Reusable Object-Oriented Software](https://read.amazon.in/kp/embed?asin=B000SEIBB8&preview=newtab&linkCode=kpe&ref_=cm_sw_r_kb_dp_W0JBDZ7VEY39TVV01N79)