Принцип разделения интерфейсов в Python — как применять
В этом уроке вы узнаете о принципе разделения интерфейса и о том, как его применять в Python.
Что такое принцип разделения интерфейсов в Python?
Принцип разделения интерфейсов — один из пяти принципов SOLID в объектно-ориентированном программировании:
- S – Single responsibility Principle (принцип единой ответственности)
- O – Open-closed Principle (принцип открытости-закрытости)
- L – Liskov Substitution Principle (принцип подстановки Лисков)
- I – Interface Segregation Principle (разделение интерфейса)
- D – Dependency Inversion Principle (инверсия зависимостей)
Интерфейс — это описание поведения, которое может выполнять объект. Например, когда вы нажимаете кнопку питания на пульте телевизора, телевизор включается, и вам не важно, как это происходит.
В объектно-ориентированном программировании интерфейс — это набор методов, которые должен иметь объект. Цель интерфейсов — позволить клиентам запрашивать правильные методы объекта через его интерфейс.
Python использует абстрактные классы в качестве интерфейсов, поскольку следует так называемому принципу утиной типизации. Принцип утиной типизации гласит: «Если оно ходит как утка и крякает как утка, то это, должно быть, утка». Другими словами, методы класса определяют, какими будут его объекты, а не тип класса.
Принцип разделения интерфейсов гласит, что интерфейс должен быть как можно меньшим с точки зрения связности. Другими словами, он должен делать ОДНУ вещь. Это не означает, что интерфейс должен иметь один метод. Интерфейс может иметь несколько связанных методов.
Например, интерфейс базы данных может иметь методы Connect() и Disconnect(), поскольку они должны работать вместе. Если интерфейс базы данных не использует оба метода, он будет неполным.
Создатель термина SOLID, объясняет принцип разделения интерфейсов, советуя: «Создавайте детализированные интерфейсы, специфичные для клиента. Клиентов не следует заставлять реализовывать интерфейсы, которые они не используют».
Пример принципа разделения интерфейса
Рассмотрим следующий пример:
- Сначала определите абстрактный класс Vehicle, который имеет два абстрактных метода: go() и Fly():
from abc import ABC, abstractmethod class Vehicle(ABC): @abstractmethod def go(self): pass @abstractmethod def fly(self): pass
- Во-вторых, определите класс Aircraft, который наследуется от класса Vehicle, и реализуйте методы go() и Fly():
class Aircraft(Vehicle): def go(self): print("Taxiing") def fly(self): print("Flying")
- В-третьих, определите класс Car, который наследуется от класса Vehicle. Поскольку автомобиль не может летать, мы вызываем исключение в методе fly():
class Car(Vehicle): def go(self): print("Going") def fly(self): raise Exception('The car cannot fly')
В этом проекте класс Car должен реализовать метод fly() из класса Vehicle, который класс Car не использует. Следовательно, такая конструкция нарушает принцип разделения интерфейсов.
Чтобы это исправить, нужно разбить класс Vehicle на мелкие и наследовать эти классы от классов Aircraft и Car:
Сначала разделите интерфейс Vehicle на два меньших интерфейса: Movable и Flyable, и унаследуйте класс Movable от класса Flyable:
class Movable(ABC): @abstractmethod def go(self): pass class Flyable(Movable): @abstractmethod def fly(self): pass
Во-вторых, наследует класс Flyable от класса Aircraft:
class Aircraft(Flyable): def go(self): print("Taxiing") def fly(self): print("Flying")
В-третьих, унаследуйте класс Movable от класса Car:
class Car(Movable): def go(self): print("Going")
В этом проекте Car нужно реализовать только тот метод go(), который ему нужен. Ему не нужно реализовывать метод Fly(), который он не использует.