Object-Oriented Design – Is Extending an Abstract Parent Class with an Abstract Child Class Bad Design?

abstract classdesign-patternsobject-orientedpython-3.x

Is it bad design to have an abstract class inherit from another abstract class?
I have a single base node (class BaseNode) and 3 possible child node types (Sink, Process, and Source).

Is the inheritance between BaseNode and SinkNode good practise? If not what is another solution?

class BaseNode (metaclass=ABCMeta):
   @abstractmethod
   def _Execute(self) -> bool:
       pass

   @abstractmethod
   def _Configuration(self) -> bool:
       pass
   def __init__(self, nodeID) 
      print(nodeID)

class SinkNode(BaseNode, metaclass=ABCMeta):
    @staticmethod
    def Type():
        return NodeType.SINK

    def __init__(self, nodeID):
        super().__init__(nodeID)

class DataWriteNode(SinkNode): 
    def _Execute(self): 
       print('Execute') 
    def _Configuration(self):
       print('Configure') 
    def __init__(self, nodeID): 
       super().__init__(nodeID) 

Best Answer

Yes, it is generally a bad design; however, I am sure they may be a case or two where it is appropriate, but I haven't seen one.

In fact, use of abstract base classes, even one, is generally bad. Inheritance is for polymorphism, not code resuse; and composition should be favored over inheritance to begin with.

With that said, we work in the real world, and tools and are here to make working software, not to be used in a exercise of spiritual purity. In some cases, it just makes practical sense to use an abstract base class for code reuse; however, it should be done with great reservation. Here are my personal rules for abstract base class use:

  1. Abstract base class can save a great deal of coding in at least 3 decedent classes
  2. The abstract base class's methods do not call any of its other methods
  3. The abstract base class aggregates multiple interfaces
  4. The abstract base class' behavior is easily overridable
  5. The abstract base class should NEVER be referenced by anyone except its inheritors, only reference the aggregated interfaces externally.

I do not think your particular use case warrants even one abstract class, never the less two.

If I were you, I would just declare several interfaces, one for each shared method, then implement everything individually in the sink and datawrite nodes. This will give you much more flexibility in the future.

Let me give you an example as to why you should not be using abstract base classes and inheritance haphazardly:

Let's say your boss tells you to make a game, the player class needs to be able to shoot any of three types of enemies: goblins, trolls, and elves. Since they act largely the same, you define a abstract Enemy class which they all inherit from. Now you make your player class which has a shoot method taking the abstract Enemy class as an argument.

This looks great, and you've saved a lot of time and code... until your boss adds a requirement that if you shoot a golden box, your health refills... obviously your golden box cannot inherit from your your Enemy class, it shares nothing in common with them except the ability to be shot. Now you have to implement two versions of the Shoot method... there goes code reuse.

Related Topic