From the course: C++ Design Patterns: Structural

Overview

- [Instructor] The adapter design pattern is a software design pattern that allows two incompatible interfaces to work together by converting one class's interface into another. This pattern is often used in situations where a client is expecting a specific interface but the object the client is trying to use as a different interface. One common use case for the adapter design pattern is when you have to integrate legacy code into your existing code base. Since you usually don't have access to the source code for this legacy code, you need to refactor your own code to match the existing APIs, right? Well, not so fast. With the adapter design pattern, you can define the class of the adapter that wraps the incompatible interface code and exposes an interface that matches your existing design. The adapter class acts as a bridge between the incompatible interfaces, allowing them to interact seamlessly with each other. Here's a UML diagram that illustrates how the adaptor design pattern works. We have our own component class which is incompatible with the legacy code's API, represented by the LegacyComponent class. But with the adaptor design pattern, we can easily integrate the two APIs using an adaptor class called LegacyAdapter. LegacyAdapter inherits from component, thus it exposes the interface known to our code base. At the same time, it wraps the LegacyComponent's instance and forwards the cause to this instance, called the adoptee. Instead of using LegacyComponent instances in our code, we'll now use LegacyAdapter objects. As a consequence, there'll be no need to refactor our code to match LegacyComponent's API. Overall, the adapter design pattern provides a simple and elegant solution for integrating incompatible APIs into our existing design. Now, are there any pitfalls to be aware of when using the adaptor design pattern? One I can think of is that we introduce a new intermediary class into our design. Although this is a better solution than manually refactoring our code to match the legacy API, there are still a risk of introducing additional complexity and unexpected behavior. And since the adapter class is essentially a wrapper around another class, it can make your code harder to understand and maintain. Additionally, the adapter pattern can result in performance overhead since it involves creating an extra layer of abstraction. So if you have access to the legacy code and there are no design constraints or other factors preventing you from refactoring the legacy code to match your design, it is preferable to adapt the incompatible interface instead of introducing an intermediary adapter class. But if you decide to use the adapter design pattern, just keep in mind that it's essential to carefully design and thoroughly test your adapter class. By doing this, you'll prevent any unexpected issues from arising and ensure that your code base stays clean. Overall, the adapter is a simple, yet powerful design pattern, that can help you seamlessly integrate incompatible interfaces into your existing design. And the benefits of being able to use a familiar interface, outweigh the potential drawbacks of using the adaptor pattern. Next, I'll walk you through an actual code example to show you the problem we're trying to solve.

Contents