diff --git a/behavioral/template/abstract_class.py b/behavioral/template/abstract_class.py new file mode 100644 index 0000000..ad3a26e --- /dev/null +++ b/behavioral/template/abstract_class.py @@ -0,0 +1,57 @@ +from abc import ABC, abstractmethod + + +class AbstractClass(ABC): + """ + The Abstract Class defines a template method that contains a skeleton of + some algorithm, composed of calls to (usually) abstract primitive + operations. + + Concrete subclasses should implement these operations, but leave the + template method itself intact. + """ + + def template_method(self) -> None: + """ + The template method defines the skeleton of an algorithm. + """ + + self.base_operation1() + self.required_operations1() + self.base_operation2() + self.hook1() + self.required_operations2() + self.base_operation3() + self.hook2() + + """These operations already have implementations.""" + + def base_operation1(self): + print("AbstractClass says: I am doing the bulk of the work") + + def base_operation2(self): + print("AbstractClass says: But I let subclasses override some operations") + + def base_operation3(self): + print("AbstractClass says: But I am doing the bulk of the work anyway") + + """These operations have to be implemented in subclasses.""" + + @abstractmethod + def required_operations1(self): + pass + + @abstractmethod + def required_operations2(self): + pass + + """ + These are "hooks." Subclasses may override them, but it's not mandatory since the hooks already have default (but + empty) implementation. Hooks provide additional extension points in some crucial places of the algorithm. + """ + + def hook1(self): + pass + + def hook2(self): + pass diff --git a/behavioral/template/concrete_classes.py b/behavioral/template/concrete_classes.py new file mode 100644 index 0000000..55346fd --- /dev/null +++ b/behavioral/template/concrete_classes.py @@ -0,0 +1,29 @@ +from behavioral.template.abstract_class import AbstractClass + + +class ConcreteClass1(AbstractClass): + """ + Concrete classes have to implement all abstract operations of the base + class. They can also override some operations with a default implementation. + """ + + def required_operations1(self): + print("ConcreteClass1 says: Implemented Operation1") + + def required_operations2(self): + print("ConcreteClass1 says: Implemented Operation2") + + +class ConcreteClass2(AbstractClass): + """ + Usually, concrete classes override only a fraction of base class' operations. + """ + + def required_operations1(self): + print("ConcreteClass2 says: Implemented Operation1") + + def required_operations2(self): + print("ConcreteClass2 says: Implemented Operation2") + + def hook1(self): + print("ConcreteCLass2 says: Overridden Hook1") diff --git a/behavioral/template/main.py b/behavioral/template/main.py new file mode 100644 index 0000000..90f77f5 --- /dev/null +++ b/behavioral/template/main.py @@ -0,0 +1,21 @@ +from behavioral.template.abstract_class import AbstractClass +from behavioral.template.concrete_classes import ConcreteClass1, ConcreteClass2 + + +def client_code(abstract_class: AbstractClass) -> None: + """ + The client code calls the template method to execute the algorithm. Client + code does not have to know the concrete class of an object it works with, as + long as it works with objects through the interface of their base class. + """ + + abstract_class.template_method() + + +if __name__ == "__main__": + print("Same client code can work with different subclasses:") + client_code(ConcreteClass1()) + print("") + + print("Same client code can work with different subclasses:") + client_code(ConcreteClass2())