Applying Factory Method and Adapter Patterns for Smart Home Devices

This course focuses on integrating the design patterns we've studied into a practical project: building a smart home system. Throughout this course, you'll learn how to create and adapt various smart home devices using the Factory Method and Adapter patterns. By the end, you will have a solid understanding of how these design patterns can make your smart home system more efficient, modular, and easier to maintain.

In this unit, we explore two essential design patterns: Factory Method and Adapter. These patterns help us create and adapt devices within a smart home system. To implement these patterns effectively, we will build the devices using the Factory Method and then adapt these devices to interact with other parts of the system using the Adapter pattern.

Quick Summary
  1. Factory Method Pattern:

    • Purpose: Encapsulates the creation of objects, making it easier to introduce new object types without altering existing code.
    • Steps:
      • Define a base class (Device) using TypeScript's abstract class.
      • Create specific device classes (Light, Fan) that extend from the base class.
      • Implement a factory class (DeviceFactory) to generate instances of these devices, using type annotations for safety.
  2. Adapter Pattern:

    • Purpose: Makes incompatible interfaces compatible, allowing objects from different classes to work together.
    • Steps:
      • Define an adapter interface (USPlugInterface) using a TypeScript interface.
      • Create adapter classes (LightAdapter, FanAdapter) that implement this interface and adapt the devices (Light, Fan) to the required interface.
Creating Devices with Factory Method Pattern

To begin with, we use the Factory Method pattern in TypeScript to define an abstract base class for our devices, derive specific device classes from this base class, and finally create a factory class responsible for generating instances of these device classes. TypeScript's type annotations and abstract classes help ensure type safety and clear contracts for subclasses.

Code Implementation

Here is the complete code for implementing the Factory Method pattern in TypeScript:

// Define the Device abstract base class
abstract class Device {
    abstract operate(): string;
}

// Define the specific Light and Fan device classes
class Light extends Device {
    operate(): string {
        return "Light is turned on";
    }
}

class Fan extends Device {
    operate(): string {
        return "Fan is spinning";
    }
}

// DeviceFactory class to generate device instances
class DeviceFactory {
    createDevice(deviceType: "light" | "fan"): Device {
        switch (deviceType) {
            case "light":
                return new Light();
            case "fan":
                return new Fan();
            default:
                throw new Error("Unknown device type");
        }
    }
}

// Example of using the factory to create devices
const factory = new DeviceFactory();

const device1 = factory.createDevice("light");
console.log(device1.operate());  // Expected Output: Light is turned on

const device2 = factory.createDevice("fan");
console.log(device2.operate());  // Expected Output: Fan is spinning

In this TypeScript implementation, the Device class is declared as abstract, enforcing that subclasses must implement the operate method. The DeviceFactory uses a union type ("light" | "fan") for the deviceType parameter, ensuring only valid device types can be passed. Type annotations throughout the code provide compile-time safety and clarity.

Adapting Devices with Adapter Pattern

Now, let's ensure our devices can interact with other parts of the system that expect a different interface. Specifically, we create an adapter to make our devices compatible with a method called plugIntoUSSocket. In TypeScript, we define the adapter interface using the interface keyword, and adapter classes implement this interface to provide the required compatibility.

Code Implementation

Here is the complete code for implementing the Adapter pattern in TypeScript:

// Define the Adapter interface
interface USPlugInterface {
    plugIntoUSSocket(): string;
}

// LightAdapter class to adapt Light to work with USPlugInterface
class LightAdapter implements USPlugInterface {
    private light: Light;

    constructor(light: Light) {
        this.light = light;
    }

    plugIntoUSSocket(): string {
        return this.light.operate();
    }
}

// Example of using the LightAdapter
const light = new Light();
const lightAdapter = new LightAdapter(light);
console.log(lightAdapter.plugIntoUSSocket());  // Expected Output: Light is turned on

// FanAdapter class to adapt Fan to work with USPlugInterface
class FanAdapter implements USPlugInterface {
    private fan: Fan;

    constructor(fan: Fan) {
        this.fan = fan;
    }

    plugIntoUSSocket(): string {
        return this.fan.operate();
    }
}

// Example of using the FanAdapter
const fan = new Fan();
const fanAdapter = new FanAdapter(fan);
console.log(fanAdapter.plugIntoUSSocket());  // Expected Output: Fan is spinning

In this TypeScript implementation, the USPlugInterface is defined using the interface keyword, specifying the plugIntoUSSocket method. The adapter classes (LightAdapter and FanAdapter) implement this interface and use type annotations for their constructor parameters and properties, ensuring type safety and clear intent.

Conclusion

Implementing the Factory Method and Adapter patterns in our smart home system allows us to create a variety of devices in a clean and reusable manner. The Factory Method pattern helps manage the creation of device objects, while the Adapter pattern ensures that these devices can seamlessly interact with different interfaces. This structured approach makes our smart home system more modular, flexible, and easier to maintain and extend in the future. TypeScript's type system and features such as abstract classes and interfaces further enhance code safety and maintainability.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal