OOP Simplified: Quick Factory Methods with Encapsulation, Abstraction, and Polymorphism in TypeScript
Tags: learning, typescript, oop, patterns
Published: 2024-07-09 18:26:05

This article explores the Factory Method design pattern in TypeScript, highlighting how it uses object-oriented programming (OOP) principles: encapsulation, abstraction, and polymorphism. 

Currently I am deepening my understanding of design patterns by studying the [catalog from Refactoring Guru](https://refactoring.guru/design-patterns/catalog).  In this article, we'll explore how to create smart home devices using the Factory Method pattern, illustrating how this approach can lead to more flexible and maintainable code.

 Follow along as I post an article for each design pattern, documenting my learning journey and sharing practical examples.


## **The Factory Method Pattern**

The Factory Method pattern is a design that defines an interface for creating objects while allowing subclasses to decide which classes to instantiate. TypeScript’s powerful type-checking and higher-order functions make it an excellent choice for implementing this pattern, resulting in robust and maintainable code.


## **Example: Smart Home Device Factory**

Imagine a smart home ecosystem. We'll create a factory function to generate objects for various smart home devices, all adhering to a unified interface.


## **Defining the Interface**

The `SmartDevice` interface ensures that all smart devices have an operate method.

```
interface SmartDevice {
  operate: () => string;
}
```


## **Creating Factory Functions**

Here's how `createLight` and `createThermostat` functions produce objects conforming to the `SmartDevice` interface.

```
const createLight = (): SmartDevice => {
  return {
    operate: () => 'Turning on the light',
  };
};

const createThermostat = (): SmartDevice => {
  return {
    operate: () => 'Adjusting the thermostat',
  };
};
```

## **Higher-Order Factory Function**

The `getDeviceFactory` function returns the appropriate factory function based on the `DeviceType`.

```
type DeviceType = 'light' | 'thermostat';

const getDeviceFactory = (type: DeviceType): (() => SmartDevice) => {
  switch (type) {
    case 'light':
      return createLight;
    case 'thermostat':
      return createThermostat;
    default:
      throw new Error("Unsupported device type");
  }
};
```


## **Instantiating Devices**

Factory functions from `getDeviceFactory` are used to create new smart device objects.

```
const lightFactory = getDeviceFactory('light');
const thermostatFactory = getDeviceFactory('thermostat');

const myLight = lightFactory();
const myThermostat = thermostatFactory();

console.log(myLight.operate()); // Outputs: Turning on the light
console.log(myThermostat.operate()); // Outputs: Adjusting the thermostat
```


## **OOP Principles Illustrated**

**Encapsulation**

Encapsulation is demonstrated by how the factory functions `createLight` and `createThermostat` manage the creation details of `SmartDevice` objects. The internal logic is hidden within each factory function, simplifying client interaction.

**Abstraction**

Abstraction is applied through the `SmartDevice` interface and the higher-order factory function `getDeviceFactory`. The interface defines a consistent contract for smart devices, and the factory function abstracts the creation process, making it easy to extend and modify.

**Polymorphism**

Polymorphism is demonstrated by handling different types of smart devices (lights and thermostats) through a single interface (SmartDevice). This consistent interface enables seamless method invocation (operate) across various device types, allowing for flexible and interchangeable usage of the smart devices.


## **Conclusion**

The Factory Method pattern highlights the power of OOP principles in creating flexible and maintainable code. Encapsulation, abstraction, and polymorphism work together to enhance system robustness, making it easier to manage and extend.

By applying these principles and patterns in your TypeScript projects, you can build scalable, efficient software systems that are easier to understand and maintain.