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. 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.
Top comments (0)