diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/structural/__init__.py b/structural/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/structural/decorator/__init__.py b/structural/decorator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/structural/decorator/component.py b/structural/decorator/component.py new file mode 100644 index 0000000..d108fdc --- /dev/null +++ b/structural/decorator/component.py @@ -0,0 +1,8 @@ +class Component: + """ + The base Component interface defines operations that can be altered by + decorators. + """ + + def operation(self) -> str: + pass diff --git a/structural/decorator/concrete_component.py b/structural/decorator/concrete_component.py new file mode 100644 index 0000000..7c8fdc9 --- /dev/null +++ b/structural/decorator/concrete_component.py @@ -0,0 +1,10 @@ +from component import Component + +class ConcreteComponent(Component): + """ + Concrete Components provide default implementations of the operations. There + might be several variations of these classes. + """ + + def operation(self) -> str: + return "ConcreteComponent" \ No newline at end of file diff --git a/structural/decorator/concrete_decorators.py b/structural/decorator/concrete_decorators.py new file mode 100644 index 0000000..e440393 --- /dev/null +++ b/structural/decorator/concrete_decorators.py @@ -0,0 +1,26 @@ +from decorator import Decorator + + +class ConcreteDecoratorA(Decorator): + """ + Concrete Decorators call the wrapped object and alter its result in some + way. + """ + + def operation(self) -> str: + """ + Decorators may call parent implementation of the operation, instead of + calling the wrapped object directly. This approach simplifies extension + of decorator classes. + """ + return f"ConcreteDecoratorA({self.component.operation()})" + + +class ConcreteDecoratorB(Decorator): + """ + Decorators can execute their behavior either before or after the call to a + wrapped object. + """ + + def operation(self) -> str: + return f"ConcreteDecoratorB({self.component.operation()})" diff --git a/structural/decorator/decorator.py b/structural/decorator/decorator.py new file mode 100644 index 0000000..bd2893b --- /dev/null +++ b/structural/decorator/decorator.py @@ -0,0 +1,23 @@ +from component import Component +from dataclasses import dataclass + + +@dataclass +class Decorator(Component): + """ + The base Decorator class follows the same interface as the other components. + The primary purpose of this class is to define the wrapping interface for + all concrete decorators. The default implementation of the wrapping code + might include a field for storing a wrapped component and the means to + initialize it. + """ + + _component: Component + + @property + def component(self) -> Component: + """The Decorator delegates all work to the wrapped component.""" + return self._component + + def operation(self) -> str: + return self._component.operation() diff --git a/structural/decorator/main.py b/structural/decorator/main.py new file mode 100644 index 0000000..b9744db --- /dev/null +++ b/structural/decorator/main.py @@ -0,0 +1,30 @@ +from component import Component +from concrete_component import ConcreteComponent +from concrete_decorators import ConcreteDecoratorA, ConcreteDecoratorB + + +def client_code(component: Component): + """ + The client code works with all objects using the Component interface. This + way it can stay independent of the concrete classes of components it works + with. + """ + + print(f"RESULT: {component.operation()}", end='') + + +if __name__ == '__main__': + # This way the client code can support both simple components... + simple = ConcreteComponent() + print("Client: I've got a simple component:") + client_code(simple) + print("\n") + + # ...as well as decorated ones. + + # Note how decorators can wrap not only simple components but the other + # decorators as well. + decorator1 = ConcreteDecoratorA(simple) + decorator2 = ConcreteDecoratorB(decorator1) + print("Client: Now I've got a decorated component:") + client_code(decorator2)