Facade design pattern

Facade (or façade) design pattern is one of the most useful patterns when working with legacy code. A facade is basically a simple interface to a potentially complex system.

Building a new house

A real world example for the facade pattern is a builder. Suppose that you want to build a new house. One way to go about it, is to engage with the excavation company, the concrete pouring company, carpenters, construction workers, the council, inspectors, etc. This may give you more flexibility but requires dealing with several parties and is quite complicated. Instead you can hire a builder who then hires and deals with sub-contractors. You only have to talk to the builder and don't need to know how he is working with sub-contractors. The builder is your simplified interface to a complex system of sub-contractors.

Temperature controller

For an example in software development world, suppose that you want to develop an application to control the temperature of a room. You need to read the temperature from a sensor connected to the PC through a serial port. The sensor uses a special protocol to encode and decode data. The request needs to be encoded before sending to the sensor, and the data which comes back from the sensor needs to be decoded. Therefore there are three subsystems: serial port controller, data encoder, and data decoder.

You do not want to deal with all of these subsystems in your temperature controller. You simply want to read the temperature. This is similar to the case that you don't want to deal with sub-contractors, and just want to tell the builder what to build. The solution is to create a facade. The facade has a simple interface as the below snippet.

class ISensor {
    virtual double GetTemperature() const = 0;
}

The class that implements this interface, has to work with encoding, decoding and the serial port. An example follows.

class SensorFacade: public ISensor {
  public:
    double GetTemperature() const override {
        // Open the serial port.
        auto serial_port_handle = OpenTheSerialPortIfNotOpen();
        
        // Prepare the message to be sent to the sensor.
        auto encoded_message = EncodeMessage("Read temperature");
        
        // Send request for data to the sensor.
        serial_port_handle->Send(encoded_message);

        // Read response from the sensor through serial port.
        auto reply_message = serial_port_handle->Read();

        // Decode the reply and read the temperature.
        auto decoded_message = DecodeMessage(replay_message);
        return GetTemperatureFromMessage(decoded_message);
    }
}

The facade implementation can be complex as it has to work with several subsystems. The main purpose of the facade pattern is to hide this complexity from the user. The TemperatureController class (see the sample code below), takes an instance of ISensor as a dependency. The controller simply reads the temperature from ISensor instance without worrying about the complexity in its implementation.

class TemperatureController {
  public:
    void TemperatureController(ISensor* sensor) {
        sensor_ = sensor;
    }

    void Update() {
        auto temperature = sensor_->GetTemperature();
        if (temperature < 20) {
            TurnOnTheHeater();
        } else if (temperature > 21) {
            TurnOffTheHeater();
        }
    }

  private:
    ISensor* sensor_;
}

In future, if the sensor is replaced with a completely different one connecting through Wi-Fi instead of the serial port, TemperatureController doesn't have to change. You will have to write a new implementation of ISensor like WifiSensorFacade and inject it into TemperatureController. This isn't an advantage specific to facade pattern though and is one of the benefits of dependency injection and using interfaces instead of concrete implementations.

The main function of the temperature controller software can be like the below snippet.

int main() {
    auto sensor = new SensorFacade();
    auto controller = new TemperatureController(sensor);
    for (;;) {
        controller->Update();
        Sleep(1000);
    }
}
 

Conclusion

If you are adding new code to a legacy code base, facade design pattern can be very useful. You can wrap the services that the new code needs from legacy code in a facade and inject it into the new code. This way, you can use the legacy code, while keeping your new code testable and readable.