In this video, move from real-world adapters to a software adapter, and see how to adopt a new class for the duck simulator.
- [Instructor] Let's implement an example of the adapter pattern in code. We'll begin with a client which is a duck simulator class, a duck interface with just two methods, quack and fly, and a mallard duck that implements the duck interface. If we test the mallard duck in the simulator, that's easy. We can just pass the duck to the test duck method and the simulator implements the duck interface so the simulator already knows how to make a duck quack and fly by calling the quack and fly methods. Now what if we have a turkey and we want to use the turkey in the duck simulator? The turkey class has a slightly different interface from a duck, so we can't just plug a turkey into the duck simulator. We can't use turkeys in the duck simulator because the duck simulator test method is expecting objects that have the duck interface with the quack and fly methods. Turkeys use a different interface with the method gobble that's used to make sound instead of quack. So we can't just pass a turkey to the test method or call quack on a turkey. That won't work. So what do we do? Well we create an adapter. We'll do that by making an adapter class that converts the turkey interface to the duck interface. As you can see here, the duck simulator is the client, the duck is the target interface, the turkey adapter is the adapter, and the turkey is the adaptee. The turkey adapter implements the duck interface so that the duck simulator will know how to make the turkey quack and fly. And the turkey adapter is composed with the turkey so it can convert calls to quack and fly into gobble and fly. Here's the turkey adapter in code. The turkey adapter class implements the duck interface. So this class must implement the two methods in this interface, quack and fly. The adapter is composed with the adaptee, that is, the turkey, and we pass the turkey into the constructor and save it there. The quack method delegates to the turkey's gobble method and likewise, the fly method delegates to the turkey's fly method. Only because turkeys don't fly as well as ducks, we're going to call the turkey's fly method five times, so it flies a bit longer. So that's how the turkey adapter adapts a turkey to the duck interface. This adapter is pretty simple. We didn't have to make too many changes to make the turkeys fit into the duck simulator, although we did modify fly just a little bit. Some adapters may require multiple calls to convert from one interface to another correctly. And some adapters may return values from the adaptee to the client. Now let's bring it all together by taking another look at the client, the duck simulator. In this version, we've added code to make a wild turkey, but we can't pass a wild turkey to test duck because the test duck method expects a duck. And the wild turkey has the wrong interface. So we create a turkey adapter and pass in the wild turkey to the adapter. Notice that because the turkey adapter implements the duck interface, we can treat the turkey adapter as a duck. Then we can pass the turkey adapter to test duck to test the turkey in the simulator. As we did in the strategy pattern, we're once again using composition to get flexibility in our design. We have a client that's expecting to make calls on a class with a given interface. So we compose that client with an adapter and we compose the adapter with the adaptee. The adaptor sits in the middle of the client and the adaptee and delegates the client's calls to the adaptee. The advantage of the adapter pattern is that you can add an adapter easily without having to modify the adaptee at all and only modify the client to add the adapter. Say you have a vendor class that you can't modify and it uses a different interface than you expect, but you really need to make the vendor class work with your system. Well, the adapter pattern makes this easy.
- What are design patterns?
- Encapsulating code that varies with the Strategy pattern
- The limitations of inheritance
- Using the Adapter pattern
- Implementing the Observer pattern
- Extending behavior with composition and the Decorator pattern
- Encapsulating iteration with the Iterator pattern
- Object creation with the Factory Method pattern
- Using design principles to guide your object-oriented design