1

I have a set of operations that need to be executed as part of large process and subclasses may slightly differ part of the step. This problem seem to be solved by using the Template Method design pattern so I have something like below.

class BaseTemplate(object):
    def perform(self)
        self.disable()
        self.update()
        self.enable()

    def disable(self):
        # Generic behavior

    def update(self):
        # Generic behavior

    def enable(self)
        # Generic behavior

class FooTemplate(BaseTemplate):
    def enable(self):
       # Foo-specific behavior

    def update(self):
       # Foo-specific behavior

class BarTemplate(BaseTemplate):
    def update(self):
       # Bar-specific behavior

However, the problem with update() is that the concrete templates just need to extra few step on top of what the base class did.

For example,

BaseTemplate

def update():
    # Get X
    # Set field A of X
    # Set field B of X

FooTemplate

def update():
    # Get Y (Y is a subclass of X)
    # Set field A of Y
    # Set field B of Y
    # Set field C of Y (Field C is specific to Y which is a subclass of X)

The problem is that setting the field of A and B are duplicated as shown above, so I could do something like this in FooTemplate

def update():
    super(FooTemplate, self).update() # Handles fields A and B
    # Set field C

But calling super classes' method in an overriding method is an anti-pattern Call Super since all subclasses' update() must call super.update() otherwise it'll break.

So, a different approach would be to have a no-op method that sets the field C in the base class and then have template subclasses implement only when needed. However, this approach seem to violate OOP updating field C is only required by FooTemplate and other subclasses of BaseTemplate should not know.

Questions:

  1. Is it correct to use template method pattern for my usage?
  2. How to handle update() which adds extra steps as templates go concrete in an OOP way?
  3. Any suggestions in general?
  • 3
    Calling super classes' method in an overriding method is not an antipattern. Requiring overriding methods to call a method in the super class is the antipattern. Read the Wikipedia article you linked. – Robert Harvey Jan 4 '18 at 22:34
  • @RobertHarvey Sorry, I'm confused. The FooTemplate.update() is required to call super's update() method at the very beginning. Isn't this fit the definition you posted? Or is the definition saying requiring overriding method to call a method in a super class that's not the overridden method is an antipattern? – Harry Cho Jan 4 '18 at 23:06
  • How much trouble is it to do this some other way? It's only an antipattern if there's a better way to do it, and the whole point of a base class is to provide base functionality. – Robert Harvey Jan 4 '18 at 23:10
  • No alternative is found so far :( I'll most likely keep the design as it is but wanted to check the community before if someone else has a suggestion for learning purpose – Harry Cho Jan 4 '18 at 23:15
  • 1
    The anti-pattern is "BaseTemplate assumes that all subclasses call super().update() and breaks otherwise". It so happening that all the subclasses call super().update() isn't problematic on its own – Caleth Jan 4 '18 at 23:58
3

Try not to fix on a specific pattern. If it looks like it doesn't fit then try another one. It is hard to say from your stripped down example, which pattern might be most appropriate, but two patterns that come to mind are pipeline and chain-of-responsibility. This way you can compose your processes from a series of small commands, and chain them together. Even better, you can modify the sequence at runtime.

You can stick with the template pattern, but I find that whatever template method structure you use, then at some point you will find a derived class where you either want a finer granularity or you suffer duplicated logic (as you are experiencing). These inheritance patterns very tightly bind the classes and a more compositional design might make your code more flexible. In general inheriting from a class in order to avoid a bit of duplicated code is something to be avoided and ends up with very complicated inheritance hierarchies that are difficult to understand.

If you can show more of the context I might be able to offer more suggestions. What are X and Y for example? where do the new values of A B and C come from, and how does Get obtain instances of X and Y?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.