Lesson 3
Automating Smart Home Lights with Command and Decorator
Building the Smart Home Automation and Lighting System

As we continue our smart home system project, this unit will focus on two design patterns: the Command pattern and the Decorator pattern. These patterns will help us create a flexible and expandable system for automation and lighting control.

Quick Summary
  1. Basic Setup:

    • Abstract Device: Define an abstract class Device and derive specific device classes (Light, Fan) from it.
    • Factory Method: Implement factory classes to generate instances of these devices.
  2. Command Pattern:

    • Purpose: Encapsulates a request as an object, allowing for parameterization, queuing, logging of requests, and support for undoable operations.
    • Components:
      • Define a ICommand interface and concrete command classes (LightOnCommand, LightOffCommand).
      • Implement a RemoteControl class to execute commands.
  3. Decorator Pattern:

    • Purpose: Adds additional functionalities dynamically to existing objects without altering their structure.
    • Components:
      • Define a decorator class (ColoredLight) to add color functionalities to a Light device.

Let’s move forward and start implementing these patterns.

Defining Smart Home Devices

Before diving into the design patterns, we need to define the devices we'll be working with. We'll start by defining an abstract class Device. Then, create specific device classes Light and Fan that inherit from Device.

C#
1// Abstract Product 2public abstract class Device 3{ 4 public abstract void On(); 5 public abstract void Off(); 6} 7 8// Concrete Device Light 9public class Light : Device 10{ 11 public override void On() => Console.WriteLine("Light is on."); 12 public override void Off() => Console.WriteLine("Light is off."); 13} 14 15// Concrete Device Fan 16public class Fan : Device 17{ 18 private int speed; 19 public override void On() => Console.WriteLine("Fan is on."); 20 public override void Off() => Console.WriteLine("Fan is off."); 21 public void SetSpeed(int speed) 22 { 23 this.speed = speed; 24 Console.WriteLine($"Fan speed set to {speed}."); 25 } 26}
Factory Method for Devices

Next, implement a factory class to generate instances of these devices.

C#
1public abstract class DeviceFactory 2{ 3 public abstract Device CreateDevice(); 4} 5 6public class LightFactory : DeviceFactory 7{ 8 public override Device CreateDevice() => new Light(); 9} 10 11public class FanFactory : DeviceFactory 12{ 13 public override Device CreateDevice() => new Fan(); 14}
Command Interface and Concrete Commands

Now, we'll use the Command pattern to create flexible automation commands. For that, let's define a ICommand interface and the concrete command classes.

C#
1public interface ICommand 2{ 3 void Execute(); 4} 5 6public class LightOnCommand : ICommand 7{ 8 private Device device; 9 10 public LightOnCommand(Device device) 11 { 12 this.device = device; 13 } 14 15 public void Execute() => device.On(); 16} 17 18public class LightOffCommand : ICommand 19{ 20 private Device device; 21 22 public LightOffCommand(Device device) 23 { 24 this.device = device; 25 } 26 27 public void Execute() => device.Off(); 28}
Remote Control Implementation

Let's also implement the remote control to execute commands.

C#
1public class RemoteControl 2{ 3 private ICommand? command; 4 5 public void SetCommand(ICommand command) 6 { 7 this.command = command; 8 } 9 10 public void PressButton() 11 { 12 if (command != null) 13 { 14 command.Execute(); 15 } 16 } 17}
Using the Command Pattern

Now, let's use the RemoteControl class in the Main program.

C#
1public class Program 2{ 3 static void Main() 4 { 5 // Create a light factory to instantiate Light devices 6 DeviceFactory lightFactory = new LightFactory(); 7 Device light = lightFactory.CreateDevice(); 8 9 // Create command instances for turning the light on and off 10 ICommand lightOn = new LightOnCommand(light); 11 ICommand lightOff = new LightOffCommand(light); 12 13 // Set commands in the remote control 14 RemoteControl remote = new RemoteControl(); 15 remote.SetCommand(lightOn); 16 17 // Simulate pressing the button on the remote control 18 remote.PressButton(); // Output: Light is on. 19 remote.SetCommand(lightOff); 20 remote.PressButton(); // Output: Light is off. 21 } 22}
Decorator Implementation

Next, we'll apply the Decorator pattern to enhance our lighting system. We'll define a decorator class ColoredLight to add color functionalities to a Light device.

C#
1public class ColoredLight : Device 2{ 3 private Device light; 4 private string color; 5 6 public ColoredLight(Device light, string color) 7 { 8 this.light = light; 9 this.color = color; 10 } 11 12 public override void On() 13 { 14 light.On(); 15 Console.WriteLine($"Light color changed to {color}."); 16 } 17 18 public override void Off() 19 { 20 light.Off(); 21 } 22}
Using the Decorator Pattern

Finally, we can use the decorator to add functionalities to devices.

C#
1public class Program 2{ 3 static void Main() 4 { 5 // Create a light factory 6 DeviceFactory lightFactory = new LightFactory(); 7 Device light = lightFactory.CreateDevice(); 8 9 // Decorate the light with red color 10 Device redLight = new ColoredLight(light, "Red"); 11 // Ensure each ColoredLight has its own base device 12 Device blueLight = new ColoredLight(lightFactory.CreateDevice(), "Blue"); 13 14 redLight.On(); // Output: Light is on. Light color changed to Red. 15 redLight.Off(); // Output: Light is off. 16 blueLight.On(); // Output: Light is on. Light color changed to Blue. 17 blueLight.Off(); // Output: Light is off. 18 } 19}
Conclusion

By using the Command and Decorator design patterns, we have crafted a smart home system that is 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. Let's bring your smart home system to life!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.