From 1826234874e5ea307fbb9df21ea7170b26023d4e Mon Sep 17 00:00:00 2001 From: Ruidy Date: Tue, 13 Oct 2020 13:14:05 +0200 Subject: [PATCH] add code example --- behavioral/visitor/component.py | 53 +++++++++++++++++++++++++++++++++ behavioral/visitor/main.py | 26 ++++++++++++++++ behavioral/visitor/visitor.py | 46 ++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 behavioral/visitor/component.py create mode 100644 behavioral/visitor/main.py create mode 100644 behavioral/visitor/visitor.py diff --git a/behavioral/visitor/component.py b/behavioral/visitor/component.py new file mode 100644 index 0000000..4138ebb --- /dev/null +++ b/behavioral/visitor/component.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod + + +# from behavioral.visitor.visitor import Visitor + + +class Component(ABC): + """ + The Component interface declares an `accept` method that should take the + base visitor interface as an argument. + """ + + @abstractmethod + def accept(self, visitor: "Visitor") -> None: + pass + + +class ConcreteComponentA(Component): + """ + Each Concrete Component must implement the `accept` method in such a way + that it calls the visitor's method corresponding to the component's class. + """ + + def accept(self, visitor: "Visitor") -> None: + """ + Note that we're calling `visitConcreteComponentA`, which matches the + current class name. This way we let the visitor know the class of the + component it works with. + """ + + visitor.visit_concrete_component_a(self) + + def exclusive_method_of_concrete_component_a(self) -> str: + """ + Concrete Components may have special methods that don't exist in their + base class or interface. The Visitor is still able to use these methods + since it's aware of the component's concrete class. + """ + return "A" + + +class ConcreteComponentB(Component): + """ + Same here: visitConcreteComponentB => ConcreteComponentB + """ + + def accept(self, visitor: "Visitor") -> None: + visitor.visit_concrete_component_b(self) + + def special_method_of_concrete_component_b(self) -> str: + return "B" diff --git a/behavioral/visitor/main.py b/behavioral/visitor/main.py new file mode 100644 index 0000000..8fa1e9a --- /dev/null +++ b/behavioral/visitor/main.py @@ -0,0 +1,26 @@ +from typing import List + +from behavioral.visitor.component import Component, ConcreteComponentA, ConcreteComponentB +from behavioral.visitor.visitor import Visitor, ConcreteVisitor1, ConcreteVisitor2 + + +def client_code(components: List[Component], visitor: Visitor): + """ + The client code can run visitor operations over any set of elements without + figuring out their concrete classes. The accept operation directs a call to + the appropriate operation in the visitor object. + """ + + for component in components: + component.accept(visitor) + + +if __name__ == "__main__": + components = [ConcreteComponentA(), ConcreteComponentB()] + print("The client code works with all visitors via the base Visitor interface:") + visitor1 = ConcreteVisitor1() + client_code(components, visitor1) + + print("It allows the same client code to work with different types of visitors:") + visitor2 = ConcreteVisitor2() + client_code(components, visitor2) diff --git a/behavioral/visitor/visitor.py b/behavioral/visitor/visitor.py new file mode 100644 index 0000000..de0c36e --- /dev/null +++ b/behavioral/visitor/visitor.py @@ -0,0 +1,46 @@ +from abc import ABC, abstractmethod + +from behavioral.visitor.component import ConcreteComponentA, ConcreteComponentB + + +class Visitor(ABC): + """ + The Visitor Interface declares a set of visiting methods that correspond to + component classes. The signature of a visiting method allows the visitor to + identify the exact class of the component that it's dealing with. + """ + + @abstractmethod + def visit_concrete_component_a(self, element: ConcreteComponentA) -> None: + pass + + @abstractmethod + def visit_concrete_component_b(self, element: ConcreteComponentB) -> None: + pass + + +""" +Concrete Visitors implement several versions of the same algorithm, which can +work with all concrete component classes. + +You can experience the biggest benefit of the Visitor pattern when using it with +a complex object structure, such as a Composite tree. In this case, it might be +helpful to store some intermediate state of the algorithm while executing +visitor's methods over various objects of the structure. +""" + + +class ConcreteVisitor1(Visitor): + def visit_concrete_component_a(self, element: ConcreteComponentA) -> None: + print(f"{element.exclusive_method_of_concrete_component_a()} + ConcreteVisitor1") + + def visit_concrete_component_b(self, element: ConcreteComponentB) -> None: + print(f"{element.special_method_of_concrete_component_b()} + ConcreteVisitor1") + + +class ConcreteVisitor2(Visitor): + def visit_concrete_component_a(self, element: ConcreteComponentA) -> None: + print(f"{element.exclusive_method_of_concrete_component_a()} + ConcreteVisitor2") + + def visit_concrete_component_b(self, element: ConcreteComponentB) -> None: + print(f"{element.special_method_of_concrete_component_b()} + ConcreteVisitor2")