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?
asked Jan 4, 2018 at 22:03
5
  • 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. Commented Jan 4, 2018 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? Commented Jan 4, 2018 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. Commented Jan 4, 2018 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 Commented Jan 4, 2018 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 Commented Jan 4, 2018 at 23:58

1 Answer 1

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?

answered Jan 4, 2018 at 23:45

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.