Lesson 1
Applying Factory Method and Adapter Patterns for Smart Home Devices in Scala
Introduction

Welcome back, Space Explorer! This lesson is the first step in our "Applying Design Patterns for Real-World Problems using Scala" course, in which we showcase real-world applications of the design patterns you've learned about in the previous courses in this path. In this first unit, we will delve into using the Factory Method and the Adapter patterns, demonstrating their power by creating and adapting smart home devices. By mastering these patterns, your smart home system will become more efficient, modular, and easier to maintain. Let’s get started!

Applying Factory Method and Adapter Patterns for Smart Home Devices

Thinking through the design before coding is crucial as it lays a solid foundation, ensuring that code is more robust, efficient, and easier to maintain. Hence, let's begin by discussing how we plan to incorporate these two design patterns in our Smart Home scenario:

  1. Factory Method Pattern:

    • Purpose: Encapsulates the creation of objects, allowing new types to be introduced with minimal changes to existing code.
    • Steps:
      • Define a trait (SmartDevice).
      • Create specific device classes (Thermostat, Light) implementing the trait.
      • Implement factory classes (ThermostatFactory, LightFactory) to generate instances of these devices.
  2. Adapter Pattern:

    • Purpose: Allows objects with incompatible interfaces to collaborate seamlessly.
    • Steps:
      • Implement a trait (SmartDevice) for adaptation purposes.
      • Create adapter classes (ThermostatAdapter) that adapt existing classes (HomeKitThermostat) to this trait.
Define Smart Home Devices

Before we dive into the intricacies of design patterns, let's set up the devices we'll be working with. We'll define a trait SmartDevice and implement concrete classes Thermostat and Light, which represent the basic functionalities of smart devices.

Scala
1// Trait representing a smart device 2trait SmartDevice: 3 def getStatus(): String 4 5// Implementation for a Thermostat 6class Thermostat extends SmartDevice: 7 def getStatus(): String = "Thermostat is set to 22°C" 8 9// Implementation for a Light 10class Light extends SmartDevice: 11 def getStatus(): String = "Light is on"

With our foundational devices in place, we can now move on to integrating the Factory Method pattern.

Define Abstract Factory Trait and Concrete Factory Classes

To efficiently create instances of smart devices in our smart home system, we utilize the Factory Method pattern. This pattern allows us to define a trait for device creation, derive specific factories, and manage instances of devices with minimal effort. By encapsulating the object creation process, our system becomes more scalable and easier to maintain.

We'll start by defining a trait DeviceFactory and creating concrete factory classes ThermostatFactory and LightFactory. These factories serve as creators for different types of SmartDevice objects, ensuring that new types can be added with minimal code changes:

Scala
1// Trait for creating smart devices 2trait DeviceFactory: 3 def createDevice(): SmartDevice 4 5// Factory for creating Thermostat instances 6class ThermostatFactory extends DeviceFactory: 7 def createDevice(): SmartDevice = Thermostat() 8 9// Factory for creating Light instances 10class LightFactory extends DeviceFactory: 11 def createDevice(): SmartDevice = Light()
Integrate and Test Factory Method

To see the Factory Method pattern in action, we will integrate and test our devices by creating instances using the factory classes and executing their functionalities in a Scala environment.

Scala
1@main def main(): Unit = 2 val thermostatFactory = ThermostatFactory() 3 val lightFactory = LightFactory() 4 5 val thermostat = thermostatFactory.createDevice() 6 val light = lightFactory.createDevice() 7 8 println(thermostat.getStatus()) // Output: Thermostat is set to 22°C 9 println(light.getStatus()) // Output: Light is on
Define Adapter Interface and Concrete Adapter Classes

In our smart home setup, devices may need to adapt to diverse interfaces over time. By incorporating the Adapter pattern, we can seamlessly integrate existing classes into our system, making them compatible with new requirements. This pattern allows objects with incompatible interfaces to work together, enhancing the flexibility of our architecture.

We'll demonstrate this by assuming we have an existing class HomeKitThermostat that doesn't conform to our SmartDevice interface. We'll create a class ThermostatAdapter to bridge HomeKitThermostat with our system's interface.

Scala
1// Existing class from a third-party library 2class HomeKitThermostat: 3 def currentTemperature(): String = "Temperature is 22°C" 4 5// Adapter adapting HomeKitThermostat to SmartDevice interface 6class ThermostatAdapter(homeKitThermostat: HomeKitThermostat) extends SmartDevice: 7 def getStatus(): String = homeKitThermostat.currentTemperature()
Integrate and Test Adapter Pattern

Let's test the Adapter pattern by applying it to our devices to ensure they function correctly within the adapted interface.

Scala
1@main def main(): Unit = 2 val homeKitThermostat = HomeKitThermostat() 3 val adaptedThermostat = ThermostatAdapter(homeKitThermostat) 4 5 println(adaptedThermostat.getStatus()) // Output: Temperature is 22°C
Conclusion

By using the Factory Method and Adapter patterns in Scala, we have enhanced the modularity and flexibility of a smart home system. You've seen how to create factories to instantiate devices and how to adapt existing classes to work within your system's interface. You'll find your design skills strengthen as you continue to experiment with these and other design patterns in real-world applications. Happy coding! 🎓

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