Introduction

Welcome back to this course on "Applying Design Patterns for Real World Problems using Scala"! 🎉 In this second lesson, we're diving into two more essential design patterns: Observer and Strategy. These patterns will help us tackle common challenges in smart home systems, enhancing our ability to create a responsive security setup and a flexible climate control system. Let's explore how Scala empowers us to implement these patterns efficiently and elegantly!

Observer Pattern for Smart Home Security System

In a smart home security system, it's common to have multiple types of sensors that need to respond to certain events, like an alarm trigger. As new sensor types are developed or installed, we want to be able to integrate them without modifying the core security system code. The Observer pattern facilitates this by decoupling the security system from the sensors, allowing for dynamic addition and removal of observers.

Define Alarm Listener Trait and Security Control Class

We'll begin by defining an AlarmListener trait, which serves as a contract for any sensor that wants to listen for alarm events. Then, we'll create the SecurityControl class, which manages the list of listeners and notifies them when the alarm is triggered.

trait AlarmListener:
  def alarm(): Unit // Method to be called when an alarm is triggered

class SecurityControl:
  private var listeners = List[AlarmListener]() // List of registered listeners

  def addListener(listener: AlarmListener): Unit =
    listeners = listener :: listeners // Add a new listener

  def removeListener(listener: AlarmListener): Unit =
    listeners = listeners.filterNot(_ == listener) // Remove an existing listener

  def triggerAlarm(): Unit =
    for listener <- listeners do listener.alarm() // Notify all listeners

In this code:

  • AlarmListener is a trait with a single method alarm, which will be implemented by all sensors.
  • SecurityControl manages a list of AlarmListener instances.
  • addListener and removeListener allow dynamic addition and removal of sensors.
  • triggerAlarm notifies all registered listeners by calling their alarm method.
Create Security System and Sensor Classes

Next, we'll create concrete sensor classes that implement the AlarmListener trait. For example, let's implement a DoorSensor and a WindowSensor.

class DoorSensor extends AlarmListener:
  def alarm(): Unit = println("🚪 Door sensor triggered alarm!")

class WindowSensor extends AlarmListener:
  def alarm(): Unit = println("🪟 Window sensor triggered alarm!")

In this snippet, DoorSensor and WindowSensor override the alarm method to provide specific responses when an alarm is triggered.

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