Suppose the following class hierarchy with a base class and some general case class that inherits from the base:
class BaseClass:
def f(self, a, b):
#do something
pass
class GeneralCase(BaseClass):
def f(self, a, b):
BaseClass.f(self, a, b)
#do something else
Now, suppose that we have a special case, in which (among others) one of the arguments of function f is predetermined and constant. One
way to implement this is to remove this argument from the argument list as follows:
class SpecialCase1(GeneralCase):
def f(self, a):
Pro’s: clean, explicit
Con’s: different method signature might cause problems when objects of different classes are used
Option 2: set default value and assert it is not changed
class SpecialCase2(GeneralCase):
def f(self, a, b=PREDEFINED_VALUE):
assert b == PREDEFINED_VALUE
GeneralCase.f(self, a, PREDEFINED_VALUE)
Pro’s: same signature
Con’s: confusing interface: “Why do we have parameter b? what happens if I change it?”
What approach would you preffer and why?
It shouldn’t be a subtype at all (cf. Liskov Substitution Principle).
There’s a property of
GeneralCase, namely:This property should hold for all subtypes of
GeneralCase(or going duck-typed, anything that should quack like aGeneralCaseregardless of class relationships). If it doesn’t hold, all code handling such objects would have to be aware of it and code around it. That’s an unacceptable burden, and largely unnecessary.If that property isn’t true for some special case, that special case shouldn’t be a subtype. It may be highly related, but either you need a more general
GeneralCaseor it’s not really a special case ofGeneralCase.If you want code reuse, use mixins. If there’s code that can operate on both
GeneralCaseandSpecialCase(e.g. because it doesn’t use that method), you can define a more general interface that doesn’t include that method (probably implicitly, we don’t need nointerfacekeyword) and thus steers clear of the aforementioned trouble.