Clients should not be forced to depend upon methods that they do not use. Interfaces belong to clients, not to hierarchies.
That is to say, if a class doesn’t use a particular method or attribute, then those methods and attributes should be segregated into a class with higher specificity.
from abc import ABC, abstractmethod
class Sprite(ABC):
@abstractmethod
def update(self):
pass
@abstractmethod
def get_keyboard_input(self, key_input):
pass
class Player(Sprite):
def update(self):
print("updated player")
def get_keboard_input(self, key_input):
print(f"recieved {key_input}")
class Enemy(Sprite):
def update(self):
print("updated enemy")
def get_keyboard_input(self, key_input):
raise NotImplementedError("enemies don't recieve key inputs")Because the Abstract Class defined the methods required to instantiate a Sprite, we are required to define get_keyboard_input for Enemy, even though Enemy does not actually use it. The issue, then, is that we are defining interfaces that are not actually implemented by the clients of that interface.
To solve this, separate the methods:
from abc import ABC, abstractmethod
class Sprite(ABC):
@abstractmethod
def update(self):
pass
class Player(ABC):
@abstractmethod
def get_keyboard_input(self, key_input):
pass
class Enemy(Sprite):
def update(self):
print("updated enemy")
class Character(Player, Sprite):
def update(self):
print("updated player")
def get_keboard_input(self, key_input):
print(f"recieved {key_input}")NowSprite and Player are base classes that provide specific OOP interfaces with Single Responsibility Principle (SRP) each.
Another example:
# printers_isp.py
from abc import ABC, abstractmethod
class Printer(ABC):
@abstractmethod
def print(self, document):
pass
@abstractmethod
def fax(self, document):
pass
@abstractmethod
def scan(self, document):
pass
class OldPrinter(Printer):
def print(self, document):
print(f"Printing {document} in black and white...")
def fax(self, document):
raise NotImplementedError("Fax functionality not supported")
def scan(self, document):
raise NotImplementedError("Scan functionality not supported")
class ModernPrinter(Printer):
def print(self, document):
print(f"Printing {document} in color...")
def fax(self, document):
print(f"Faxing {document}...")
def scan(self, document):
print(f"Scanning {document}...")Should be modified to:
# printers_isp.py
from abc import ABC, abstractmethod
class Printer(ABC):
@abstractmethod
def print(self, document):
pass
class Fax(ABC):
@abstractmethod
def fax(self, document):
pass
class Scanner(ABC):
@abstractmethod
def scan(self, document):
pass
class OldPrinter(Printer):
def print(self, document):
print(f"Printing {document} in black and white...")
class NewPrinter(Printer, Fax, Scanner):
def print(self, document):
print(f"Printing {document} in color...")
def fax(self, document):
print(f"Faxing {document}...")
def scan(self, document):
print(f"Scanning {document}...")