From a46fcbfdc63cdb5cf13752cd4c47db0567664205 Mon Sep 17 00:00:00 2001 From: Ruidy Date: Sun, 27 Sep 2020 14:26:34 +0200 Subject: [PATCH] add facade code example --- README.md | 1 + structural/README.md | 1 + structural/__init__.py | 0 structural/facade/README.md | 32 +++++++++++++++++++++++++++++ structural/facade/__init__.py | 0 structural/facade/facade.py | 36 +++++++++++++++++++++++++++++++++ structural/facade/main.py | 24 ++++++++++++++++++++++ structural/facade/subsystems.py | 24 ++++++++++++++++++++++ 8 files changed, 118 insertions(+) create mode 100644 structural/__init__.py create mode 100644 structural/facade/README.md create mode 100644 structural/facade/__init__.py create mode 100644 structural/facade/facade.py create mode 100644 structural/facade/main.py create mode 100644 structural/facade/subsystems.py diff --git a/README.md b/README.md index 14a1b8f..7eaba59 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,4 @@ - [Adapter](structural/adapter/README.md) - [Bridge](structural/bridge/README.md) - [Composite](structural/composite/README.md) + - [Facade](structural/facade/README.md) diff --git a/structural/README.md b/structural/README.md index d260816..2a4ca3f 100644 --- a/structural/README.md +++ b/structural/README.md @@ -5,3 +5,4 @@ Structural patterns explain how to assemble objects and classes into larger stru - [Adapter](adapter/README.md) - [Bridge](bridge/README.md) - [Composite](composite/README.md) +- [Facade](facade/README.md) diff --git a/structural/__init__.py b/structural/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/structural/facade/README.md b/structural/facade/README.md new file mode 100644 index 0000000..09d5089 --- /dev/null +++ b/structural/facade/README.md @@ -0,0 +1,32 @@ +# Facade Pattern + +Facade is a structural design pattern that provides a simplified (but limited) interface to a complex system of classes, library or framework. + +## Summary + +Facade is a structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes. + +## Problem + +Imagine that you must make your code work with a broad set of objects that belong to a sophisticated library or framework. Ordinarily, you’d need to initialize all of those objects, keep track of dependencies, execute methods in the correct order, and so on. + +As a result, the business logic of your classes would become tightly coupled to the implementation details of 3rd-party classes, making it hard to comprehend and maintain. + +## Solution + +A facade is a class that provides a simple interface to a complex subsystem which contains lots of moving parts. A facade might provide limited functionality in comparison to working with the subsystem directly. However, it includes only those features that clients really care about. + +Having a facade is handy when you need to integrate your app with a sophisticated library that has dozens of features, but you just need a tiny bit of its functionality. + +For instance, an app that uploads short funny videos with cats to social media could potentially use a professional video conversion library. However, all that it really needs is a class with the single method `encode(filename, format)`. After creating such a class and connecting it with the video conversion library, you’ll have your first facade. + +## How to Implement + +1. Check whether it’s possible to provide a simpler interface than what an existing subsystem already provides. You’re + on the right track if this interface makes the client code independent from many of the subsystem’s classes. + +1. Declare and implement this interface in a new facade class. The facade should redirect the calls from the client code to appropriate objects of the subsystem. The facade should be responsible for initializing the subsystem and managing its further life cycle unless the client code already does this. + +1. To get the full benefit from the pattern, make all the client code communicate with the subsystem only via the facade. Now the client code is protected from any changes in the subsystem code. For example, when a subsystem gets upgraded to a new version, you will only need to modify the code in the facade. + +1. If the facade becomes too big, consider extracting part of its behavior to a new, refined facade class \ No newline at end of file diff --git a/structural/facade/__init__.py b/structural/facade/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/structural/facade/facade.py b/structural/facade/facade.py new file mode 100644 index 0000000..a631c08 --- /dev/null +++ b/structural/facade/facade.py @@ -0,0 +1,36 @@ +from structural.facade.subsystems import Subsystem1, Subsystem2 + + +class Facade: + """ + The Facade class provides a simple interface to the complex logic of one or + several subsystems. The Facade delegates the client requests to the + appropriate objects within the subsystem. The Facade is also responsible for + managing their lifecycle. All of this shields the client from the undesired + complexity of the subsystem. + """ + + def __init__(self, subsystem1: Subsystem1, subsystem2: Subsystem2) -> None: + """ + Depending on your application's needs, you can provide the Facade with + existing subsystem objects or force the Facade to create them on its + own. + """ + self._subsystem1 = subsystem1 or Subsystem1() + self._subsystem2 = subsystem2 or Subsystem2() + + def operation(self) -> str: + """ + The Facade's methods are convenient shortcuts to the sophisticated + functionality of the subsystems. However, clients get only to a fraction + of a subsystem's capabilities. + """ + results = [] + results.append("Facade initializes subsystems:") + results.append(self._subsystem1.operation1()) + results.append(self._subsystem2.operation1()) + results.append("Facade orders subsystems to perform the action:") + results.append(self._subsystem1.operation_n()) + results.append(self._subsystem2.operation_z()) + + return '\n'.join(results) diff --git a/structural/facade/main.py b/structural/facade/main.py new file mode 100644 index 0000000..8e6c417 --- /dev/null +++ b/structural/facade/main.py @@ -0,0 +1,24 @@ +from structural.facade.facade import Facade +from structural.facade.subsystems import Subsystem1, Subsystem2 + + +def client_code(facade: Facade) -> None: + """ + The client code works with complex subsystems through a simple interface + provided by the Facade. When a facade manages the lifecycle of the + subsystem, the client might not even know about the existence of the + subsystem. This approach lets you keep the complexity under control. + """ + + print(facade.operation(), end='') + + +if __name__ == '__main__': + # The client code may have some of the subsystem's objects already created. + # In this case, it might be worthwhile to initialize the Facade with these + # objects instead of letting the Facade create new instances. + subsystem1 = Subsystem1() + subsystem2 = Subsystem2() + + facade = Facade(subsystem1, subsystem2) + client_code(facade) diff --git a/structural/facade/subsystems.py b/structural/facade/subsystems.py new file mode 100644 index 0000000..8bf8e65 --- /dev/null +++ b/structural/facade/subsystems.py @@ -0,0 +1,24 @@ +class Subsystem1: + """ + The Subsystem can accept requests either from the facade or client directly. + In any case, to the Subsystem, the Facade is yet another client, and it's + not a part of the Subsystem. + """ + + def operation1(self) -> str: + return "Subsystem1: Ready!" + + def operation_n(self) -> str: + return "Subsystem1: Go!" + + +class Subsystem2: + """ + Some facades can work with multiple subsystems at the same time. + """ + + def operation1(self) -> str: + return "Subsystem2: Get ready!" + + def operation_z(self) -> str: + return "Subsystem2: Fire!"