Building the Smart Home Automation and Lighting System

In this unit, we'll implement two essential design patterns: the Command pattern and the Decorator pattern, aimed at enhancing our smart home automation and lighting system. These patterns help create a more flexible and expandable system for automation and lighting control.

Quick Summary

TypeScript provides explicit interfaces and type annotations, which help define clear contracts and improve code safety. When implementing design patterns in TypeScript, it's idiomatic to use interfaces or abstract classes to define the structure of components and commands.

  1. Command Pattern:

    • Purpose: Encapsulates a request as an object, enabling parameterization, queuing, logging, and support for undoable operations.
    • Steps:
      • Define a Command interface with an execute method.
      • Create concrete command classes (LightOnCommand, LightOffCommand) that implement this interface and perform specific actions.
      • Implement a receiver class (Light) with methods to turn the light on and off.
      • Use an invoker class (RemoteControl) to store and execute commands, using type annotations for properties and method parameters.
  2. Decorator Pattern:

    • Purpose: Dynamically adds additional functionality to objects without altering their structure.
    • Steps:
      • Define a component contract using an interface or abstract class (e.g., LightComponent).
      • Create a base decorator class (LightDecorator) that implements the component contract and holds a reference to a component object.
      • Develop concrete decorators (ColorChangeDecorator) to extend the functionalities of the basic component, using type annotations throughout.
Implementing the Command Pattern

Using the Command pattern in TypeScript, we define our commands and receivers with explicit interfaces and type annotations. This approach enables flexible and parameterizable control over our smart home system while ensuring type safety and clear contracts between components.

Code Implementation for Command Pattern

Here is the complete code for implementing the Command pattern using TypeScript:

// Command interface
interface Command {
    execute(): void;
}

// Receiver class encapsulating the light functionalities
class Light {
    turnOn(): void {
        console.log("The light is on");
    }

    turnOff(): void {
        console.log("The light is off");
    }
}

// Concrete command classes for turning the light on and off
class LightOnCommand implements Command {
    private light: Light;

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

    execute(): void {
        this.light.turnOn();
    }
}

class LightOffCommand implements Command {
    private light: Light;

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

    execute(): void {
        this.light.turnOff();
    }
}

// Invoker class to store and execute commands
class RemoteControl {
    private slots: { [key: string]: Command } = {};

    setCommand(commandName: string, command: Command): void {
        this.slots[commandName] = command;
    }

    pressButton(commandName: string): void {
        const command = this.slots[commandName];
        if (command) {
            command.execute();
        } else {
            console.log(`No command found for ${commandName}`);
        }
    }
}

// Example of setting up and using the command pattern
const livingRoomLight = new Light();

const lightOnCommand = new LightOnCommand(livingRoomLight);
const lightOffCommand = new LightOffCommand(livingRoomLight);

const remoteControl = new RemoteControl();

remoteControl.setCommand("light_on", lightOnCommand);
remoteControl.setCommand("light_off", lightOffCommand);

remoteControl.pressButton("light_on");  // Expected Output: The light is on
remoteControl.pressButton("light_off"); // Expected Output: The light is off
Implementing the Decorator Pattern

By using the Decorator pattern in TypeScript, we can dynamically enhance our smart home's lighting system with additional features. TypeScript's interfaces and type annotations help define clear contracts for components and decorators, ensuring type safety and extensibility.

Code Implementation for Decorator Pattern

Here is the complete code for implementing the Decorator pattern using TypeScript:

// Component contract using an interface
interface LightComponent {
    operate(): string;
}

// Basic Light class implementing the component contract
class Light implements LightComponent {
    operate(): string {
        return "Basic light operation";
    }
}

// Base Decorator class implementing the component contract
class LightDecorator implements LightComponent {
    protected decoratedLight: LightComponent;

    constructor(decoratedLight: LightComponent) {
        this.decoratedLight = decoratedLight;
    }

    operate(): string {
        return this.decoratedLight.operate();
    }
}

// Concrete Decorator for adding color-changing functionality
class ColorChangeDecorator extends LightDecorator {
    operate(): string {
        return `${super.operate()} with color change`;
    }
}

// Example of using the decorator pattern
const basicLight: LightComponent = new Light();
console.log(basicLight.operate());  // Expected Output: Basic light operation

const colorLight: LightComponent = new ColorChangeDecorator(basicLight);
console.log(colorLight.operate());  // Expected Output: Basic light operation with color change

Implementing the Command and Decorator patterns in our smart home system using TypeScript provides flexibility, scalability, and type safety. The Command pattern encapsulates requests as objects, allowing for parameterizable and undoable operations. The Decorator pattern dynamically adds functionalities, making the system adaptable and extensible.

Summary

In summary, the integration of both Command and Decorator design patterns significantly enhances the functionality and flexibility of a smart home automation and lighting system. By allowing requests to be encapsulated as objects, the Command pattern provides a robust framework for executing, queuing, and undoing actions with ease. Meanwhile, the Decorator pattern affords the ability to extend object functionalities dynamically, supporting ongoing scalability and adaptability without altering existing structures. Together, these patterns contribute to a sophisticated, maintainable, and extensible smart home solution.

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