Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Alternative that doesn't violate the Liskov substitution principle

Considering the following structure of classes:

from abc import ABC, abstractmethod


class ModelSettings(ABC):
    ...

class BlueModelSettings(ModelSettings):
    ...

class RedModelSettings(ModelSettings):
    ...


class Model(ABC):
    @abstractmethod
    def compute(self, settings: ModelSettings):
        ...

class BlueModel(Model):
    def compute(self, settings: BlueModelSettings):
        ...

class RedModel(Model):
    def compute(self, settings: RedModelSettings):
        ...

MyPy is complaining that the implementations of compute in BlueModel and RedModel violate the Liskov substitution principle. I understand this and also understand why it makes sense.

My question is, instead of the above structure, what would be another approach that would satisfy the following requirements:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

  • base model has a contract that compute should receive settings, not apples or oranges
  • but each specific model should accept only settings relevant to its own model kind

In a way, I essentially want that a subclass’ method argument is stricter than what its base class stipulates, which goes directly against the Liskov principle. Which is what’s telling me there might be a more suitable approach.

Note:

  • Model is library code so it can’t know of e.g. BlueModelSettings which is client code

>Solution :

You need your Model to be generic in the type of settings.

T = TypeVar('T', bound=ModelSettings)

class Model(ABC, Generic[T]):
    @abstractmethod
    def compute(self, settings: T):
        ...

class BlueModel(Model[BlueModelSettings]):
    def compute(self, settings: BlueModelSettings):
        ...

class RedModel(Model[RedModelSettings]):
    def compute(self, settings: RedModelSettings):
        ...
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading