From cbcb57d42b27adb457d05c6014b035ec96d334cd Mon Sep 17 00:00:00 2001 From: Ruidy Nemausat Date: Tue, 15 Sep 2020 17:48:08 +0200 Subject: [PATCH] adapter example --- README.md | 2 + creational/README.md | 2 + structural/README.md | 5 +++ structural/adapter/README.md | 36 +++++++++++++++++ structural/adapter/via_composition.py | 57 +++++++++++++++++++++++++++ structural/adapter/via_inheritance.py | 54 +++++++++++++++++++++++++ 6 files changed, 156 insertions(+) create mode 100644 structural/README.md create mode 100644 structural/adapter/README.md create mode 100644 structural/adapter/via_composition.py create mode 100644 structural/adapter/via_inheritance.py diff --git a/README.md b/README.md index e738b5a..e6f7661 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,5 @@ - [Builder](creational/builder/README.md) - [Prototype](creational/prototype/README.md) - [Singleton](creational/singleton/README.md) +- [Structural Patterns](structural/README.md) + - [Adapter](structural/adapter/README.md) diff --git a/creational/README.md b/creational/README.md index c85b7f2..7ec7c96 100644 --- a/creational/README.md +++ b/creational/README.md @@ -1,5 +1,7 @@ # Creational Patterns +Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code. + - [Factory Method](factory-method/README.md) - [Abstract Factory](abstract-factory/README.md) - [Builder](builder/README.md) diff --git a/structural/README.md b/structural/README.md new file mode 100644 index 0000000..8b44d24 --- /dev/null +++ b/structural/README.md @@ -0,0 +1,5 @@ +# Structural Patterns + +Structural patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient. + +- [Adapter](adapter/README.md) diff --git a/structural/adapter/README.md b/structural/adapter/README.md new file mode 100644 index 0000000..0238b74 --- /dev/null +++ b/structural/adapter/README.md @@ -0,0 +1,36 @@ +# Adapter + +Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate. + +## Summary + +The Adapter acts as a wrapper between two objects. It catches calls for one object and transforms them to format and interface recognizable by the second object. + +## Problem + +Imagine that you’re creating a stock market monitoring app. The app downloads the stock data from multiple sources in XML format and then displays nice-looking charts and diagrams for the user. + +At some point, you decide to improve the app by integrating a smart 3rd-party analytics library. But there’s a catch: the analytics library only works with data in JSON format. + +You could change the library to work with XML. However, this might break some existing code that relies on the library. And worse, you might not have access to the library’s source code in the first place, making this approach impossible. + +## Solution + +You can create an adapter. This is a special object that converts the interface of one object so that another object can understand it. + +## How to Implement + +1. Make sure that you have at least two classes with incompatible interfaces: + + - A useful service class, which you can’t change (often 3rd-party, legacy or with lots of existing dependencies). + - One or several client classes that would benefit from using the service class. + +1. Declare the client interface and describe how clients communicate with the service. + +1. Create the adapter class and make it follow the client interface. Leave all the methods empty for now. + +1. Add a field to the adapter class to store a reference to the service object. The common practice is to initialize this field via the constructor, but sometimes it’s more convenient to pass it to the adapter when calling its methods. + +1. One by one, implement all methods of the client interface in the adapter class. The adapter should delegate most of the real work to the service object, handling only the interface or data format conversion. + +1. Clients should use the adapter via the client interface. This will let you change or extend the adapters without affecting the client code. diff --git a/structural/adapter/via_composition.py b/structural/adapter/via_composition.py new file mode 100644 index 0000000..19f481d --- /dev/null +++ b/structural/adapter/via_composition.py @@ -0,0 +1,57 @@ +class Target: + """ + The Target defines the domain-specific interface used by the client code. + """ + + def request(self) -> str: + return "Target: The default target's behavior." + + +class Adaptee: + """ + The Adaptee contains some useful behavior, but its interface is + incompatible with the existing client code. The Adaptee needs some + adaptation before the client code can use it. + """ + + def specific_request(self) -> str: + return ".eetpadA eht fo roivaheb laicepS" + + +class Adapter(Target): + """ + The Adapter makes the Adaptee's interface compatible with the Target's + interface via composition. + """ + + def __init__(self, adaptee: Adaptee) -> None: + self.adaptee = adaptee + + def request(self) -> str: + return f"Adapter: (TRANSLATED) {self.adaptee.specific_request()[::-1]}" + + +def client_code(target: Target) -> None: + """ + The client code supports all classes that follow the Target interface. + """ + + print(target.request(), end="") + + +if __name__ == "__main__": + print("Client: I can work just fine with the Target objects:") + target = Target() + client_code(target) + print("\n") + + adaptee = Adaptee() + print( + "Client: The Adaptee class has a weird interface. " + "See, I don't understand it:" + ) + print(f"Adaptee: {adaptee.specific_request()}", end="\n\n") + + print("Client: But I can work with it via the Adapter:") + adapter = Adapter(adaptee) + client_code(adapter) \ No newline at end of file diff --git a/structural/adapter/via_inheritance.py b/structural/adapter/via_inheritance.py new file mode 100644 index 0000000..0ac2c65 --- /dev/null +++ b/structural/adapter/via_inheritance.py @@ -0,0 +1,54 @@ +class Target: + """ + The Target defines the domain-specific interface used by the client code. + """ + + def request(self) -> str: + return "Target: The default target's behavior." + + +class Adaptee: + """ + The Adaptee contains some useful behavior, but its interface is + incompatible with the existing client code. The Adaptee needs some + adaptation before the client code can use it. + """ + + def specific_request(self) -> str: + return ".eetpadA eht fo roivaheb laicepS" + + +class Adapter(Target, Adaptee): + """ + The Adapter makes the Adaptee's interface compatible with the Target's + interface via multiple inheritance. + """ + + def request(self) -> str: + return f"Adapter: (TRANSLATED) {self.specific_request()[::-1]}" + + +def client_code(target: "Target") -> None: + """ + The client code supports all classes that follow the Target interface. + """ + + print(target.request(), end="") + + +if __name__ == "__main__": + print("Client: I can work just fine with the Target objects:") + target = Target() + client_code(target) + print("\n") + + adaptee = Adaptee() + print( + """Client: The Adaptee class has a weird interface. See, I don't + understand it:""" + ) + print(f"Adaptee: {adaptee.specific_request()}", end="\n\n") + + print("Client: But I can work with it via the Adapter:") + adapter = Adapter() + client_code(adapter)