By the end of this lesson, you should be able to...
- Explain why Structural design patterns are important in software development
- Describe:
- the Adapter pattern
- the software construction problem each is intended to solve
- potential use cases
- Assess:
- the suitability of patterns to solve a given problem
- the trade offs (pros/cons)
- Implement basic examples
Structural design patterns ease design by identifying a simple way to establish relationships between entities.
They can assemble objects and classes into larger structures while keeping these structures flexible and efficient.
Analogy
If you've traveled to Europe and you are from America, or vice versa, you noticed that you couldn't plug in most of your electronics to charge the,. The power plugs and sockets are different. You need to get a plug adapter that has the American-style socket and the European-style.Scenario
You want to create an app the monitors the stock market. Apparently Game Stop's stock is growing like crazy and you want to check that out.Your app downloads the data from multiple sources in XML format. And you want to display this data in nice-looking charts for your users.
You got traction with your app and decided you wanted to add a 3-rd party library for analytics. But the library can only work with data in JSON format.
What can you do? What are your thoughts? 🤔
Share them in the chat.
Possible Solution
You can create an adapter. One that will transform you data from XML to JSON so that you code can directly communicate with the analytics library. Therefore, you should make your code communicate to the library only through this adapter.Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
Provides a link between two elements that otherwise would not fit with each other by wrapping the adaptee with a class that supports the interface required by a client.
It decouples the client from the class of the targeted object.
The key idea in this pattern is to work through a separate adapter that adapts the interface of an (already existing) class without changing it.
Adapter is typically composed of four main actors:
- Adaptee - Defines an existing interface that needs adapting.
- Adapter - Adapts/converts the incompatible interface of a class (Adaptee) into another interface that clients require.
- Target - Defines the domain-specific interface that the Client uses.
- Client - Collaborates with objects conforming to the Target interface.
And how they all work happens in 4 steps:
- Client makes a request.
- The adapter takes the request and searches for the method needed to be called with knowledge from both API's
- The component gets the request, does the job and returns it to the adapter.
- The adapter translates the response.
- The response is handled to the client.
The Adapter object does the actual work - it "wraps" the original object and adds new requirements specified by the Target.
The pattern is implemented correctly when the adapter allows a component to be integrated into the application without requiring modification of the existing application modules or the component.
Best Practices in Swift
Use Protocols - The design of iOS protocols does not perfectly match the description of the Adapter pattern, though it achieves the goal of the pattern: allowing classes with otherwise incompatible interfaces to work together.
Use Class Extensions - The most elegant way to implement the Adapter pattern is with a Swift extension.
Extensions allow you to add functionality to classes that you are unable to modify.
This functionality includes adding conformance to a protocol, which is perfectly suited to implementing the Adapter pattern.
The Adapter pattern solves problems like:
- How can a class be reused that does not have an interface that a client requires?
- How can classes that have incompatible interfaces work together?
- How can an alternative interface be provided for a class?
Problems that Adapter solves arise when an existing system needs to integrate a new component that has a similar function but that doesn’t present a common interface and that cannot be modified.
Another key use of Adapter arises when you do not have access to the original source code. Incompatible code can be introduced into a project when:
- a third-party component is used
- or when you depend on the code produced by another development team working on a related project.
Pros | Cons |
---|---|
Integrate components when you can't modify source code | Code complexity increases |
Single Responsibility Principle | Forcing non intended integrations |
New functionality without breaking code |
Classes, modules, and functions can’t always be modified, especially if they’re from a third-party library. Sometimes you have to adapt instead!
Use Adapter...
- when you need to integrate a component that provides similar functionality to other components in the application but that uses an incompatible API.
- during the implementation of third-party classes when there is a mismatch in the interface and it does not tally with the rest of application code.
Adapter is also very often used in systems based on some legacy code.
Complete this activity to learn how to implement the pattern.
In groups, pick a pattern from the Structural Patterns category to explain it.
25 min Make a copy of this template and fill it out with your group.
15 min each group Share the slides and explain the code snippet for the example implementation.
Finish the worksheet with all the patterns covered up to today and turn in the complete version in Gradescope.
Continued Stretch Challenge: Extend the Media Player app you created and improved in previous classes by implementing the following using either the Adapter or the Decorator pattern:
- Add functionality so that the user can skip to the
Next
orPrevious
video clip (This will require that you have a collection of several video clips)