Introduction

Welcome to the third lesson of our "Applying Design Patterns for Real World Problems using Scala" course! In this lesson, we'll delve into two powerful design patterns: the Command pattern and the Decorator pattern. These patterns will empower you to design a robust and flexible smart home automation and lighting control system in Scala 3. Let's embark on this journey to create a system that’s as adaptable as it is efficient! 🚀

Lesson overview

Here's a brief overview of what we'll be doing in this lesson:

  1. Basic Setup:

    • Abstract Device: Define a trait Device and create specific device classes (Light, Fan) that extend from it.
    • Factory Method: Leverage companion objects to serve as factories for generating instances of these devices.
  2. Command Pattern:

    • Purpose: Encapsulates a request as an object, facilitating parameterization, queuing, logging, and support for undoable operations.
    • Components:
      • Define a Command trait and concrete command classes (LightOnCommand, LightOffCommand).
      • Implement a RemoteControl class for command executions.
  3. Decorator Pattern:

    • Purpose: Dynamically adds functionalities to existing objects without altering their structure.
    • Components:
      • Use a decorator class (ColoredLight) to add color capabilities to a Light device.

Let’s dive into the Scala code and bring these patterns to life! 🎉

Defining Smart Home Devices

Let's start by defining the devices we'll be working with. We'll create a trait Device and extend it with concrete device classes Light and Fan.

This code defines the foundational elements of our smart home system:

  • The Device trait establishes a common interface with on() and off() methods.
  • The Light class extends Device with basic functionality to turn the light on and off.
  • The Fan class extends Device and adds the ability to set the fan's speed through the setSpeed() method.
Factory Method for Devices

We'll now implement factory methods using a companion object to create instances of our devices.

By introducing DeviceFactory, we encapsulate the creation of Device instances, promoting better code organization and flexibility. The factory methods createLight() and createFan() simplify object instantiation.

Applying the Command Pattern: Command Interface

Next, we'll apply the Command pattern to create flexible automation commands. First, we define a Command trait as the foundation for our system, alongside concrete command classes for light operations:

In this snippet, we establish the Command trait and implement LightOnCommand and LightOffCommand to encapsulate turning a light on and off.

Applying the Command Pattern: RemoteControl Invoker

Next, we introduce the RemoteControl class to handle the execution of commands.

The RemoteControl class acts as the invoker in the Command pattern:

  • It holds a reference to a Command object.
  • The setCommand() method assigns a command to the remote.
  • The pressButton() method executes the assigned command, if any.
Applying the Command Pattern: Sample Usage

We now put the command pattern into action within our main Scala program.

This example demonstrates:

  • Creating a Light device.
  • Instantiating LightOnCommand and LightOffCommand with the light device.
  • Using the RemoteControl to set and execute commands, turning the light on and off.
Applying the Decorator Pattern: Decorator Definition

Now, we'll enhance our lighting system using the Decorator pattern.

We start with the definition of a ColoredLight decorator class to add color functionality to our lights:

In this simple snippet:

  • ColoredLight acts as a wrapper around a Device object (such as a Light), adding color functionality.
  • It overrides the on() method to first turn on the light and then add the color-changing functionality.
  • The off() method simply delegates to the wrapped device.
Applying the Decorator Pattern: Using the Decorator

Finally, let's apply our ColoredLight decorator to dynamically add color functionality to light devices.

This code shows how:

  • We create ColoredLight instances to enhance lights with color.
  • When on() is called, it turns on the light and changes its color, demonstrating dynamic addition of functionality.
Conclusion

By integrating the Command and Decorator design patterns, you've built a smart home system that is both modular and flexible. These patterns enable you to extend functionality and dynamically add features without modifying the core structure. Now it's your turn—get hands-on by implementing these patterns yourself and watch your smart home system come alive! 🌟

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