This course focuses on integrating the design patterns we've studied into a practical project: building a smart home system. Throughout this course, you'll learn how to create and adapt various smart home devices using the Factory Method and Adapter patterns. By the end, you will have a solid understanding of how these design patterns can make your smart home system more efficient, modular, and easier to maintain.
In this unit, we explore two essential design patterns: Factory Method and Adapter. These patterns help us create and adapt devices within a smart home system. To effectively implement these patterns, we will build the devices using the Factory Method and then adapt these devices to interact with other parts of the system using the Adapter pattern.
-
Factory Method Pattern:
- Purpose: Encapsulates the creation of objects, making it easier to introduce new object types without altering existing code.
- Steps:
- Define a base class (
Device
). - Create specific device classes (
Light
,Fan
) extending from the base class. - Implement a factory class (
DeviceFactory
) to generate instances of these devices.
- Define a base class (
-
Adapter Pattern:
- Purpose: Makes incompatible interfaces compatible, allowing objects from different classes to work together.
- Steps:
- Define an adapter interface (
USPlugInterface
). - Create adapter classes (
LightAdapter
,FanAdapter
) that implement this interface and adapt the devices (Light
,Fan
) to the required interface.
- Define an adapter interface (
To begin with, we use the Factory Method pattern in JavaScript to define a base class for our devices, derive specific device classes from this base class, and finally create a factory class responsible for generating instances of these device classes.
Here is the complete code for implementing the Factory Method pattern:
JavaScript1// Define the Device base class 2class Device { 3 operate() { 4 throw new Error('Method must be implemented'); 5 } 6} 7 8// Define the specific Light and Fan device classes 9class Light extends Device { 10 operate() { 11 return "Light is turned on"; 12 } 13} 14 15class Fan extends Device { 16 operate() { 17 return "Fan is spinning"; 18 } 19} 20 21// DeviceFactory class to generate device instances 22class DeviceFactory { 23 createDevice(deviceType) { 24 switch (deviceType) { 25 case "light": 26 return new Light(); 27 case "fan": 28 return new Fan(); 29 default: 30 throw new Error("Unknown device type"); 31 } 32 } 33} 34 35// Example of using the factory to create devices 36const factory = new DeviceFactory(); 37 38const device1 = factory.createDevice("light"); 39console.log(device1.operate()); // Expected Output: Light is turned on 40 41const device2 = factory.createDevice("fan"); 42console.log(device2.operate()); // Expected Output: Fan is spinning
The factory
object is created using the DeviceFactory
class. We then use the createDevice
method to create instances for "light"
and "fan"
. The respective operate
methods are called, displaying "Light is turned on" and "Fan is spinning" as the outputs.
Now, let's ensure our devices can interact with other parts of the system that expect a different interface. Specifically, we create an adapter to make our devices compatible with a method called plugIntoUSSocket
.
Here is the complete code for implementing the Adapter pattern:
JavaScript1// Define the Adapter interface 2class USPlugInterface { 3 plugIntoUSSocket() { 4 throw new Error('Method must be implemented'); 5 } 6} 7 8// LightAdapter class to adapt Light to work with USPlugInterface 9class LightAdapter extends USPlugInterface { 10 constructor(light) { 11 super(); 12 this.light = light; 13 } 14 15 plugIntoUSSocket() { 16 return this.light.operate(); 17 } 18} 19 20// Example of using the LightAdapter 21const light = new Light(); 22const lightAdapter = new LightAdapter(light); 23console.log(lightAdapter.plugIntoUSSocket()); // Expected Output: Light is turned on 24 25// FanAdapter class to adapt Fan to work with USPlugInterface 26class FanAdapter extends USPlugInterface { 27 constructor(fan) { 28 super(); 29 this.fan = fan; 30 } 31 32 plugIntoUSSocket() { 33 return this.fan.operate(); 34 } 35} 36 37// Example of using the FanAdapter 38const fan = new Fan(); 39const fanAdapter = new FanAdapter(fan); 40console.log(fanAdapter.plugIntoUSSocket()); // Expected Output: Fan is spinning
We create a Light
and a Fan
object and then instances of LightAdapter
and FanAdapter
by passing the respective device objects to their constructors. The plugIntoUSSocket
method is called on the adapters, which internally call the operate
methods of the devices.
Implementing the Factory Method and Adapter patterns in our smart home system allows us to create a variety of devices in a clean and reusable manner. The Factory Method pattern helps in managing the creation of device objects, while the Adapter pattern ensures that these devices can seamlessly interact with different interfaces. This structured approach makes our smart home system more modular, flexible, and easier to maintain and extend in the future.