126 Глава 4 • Классы и ООП
У нас был класс
BaseService
, который определял общий интерфейс и не-
сколько конкретных реализаций. Конкретные реализации делают разные
вещи, но все они обеспечивают тот же самый интерфейс (
MockService
,
RealService
и т. д.). Чтобы более четко проявить взаимосвязи, все кон-
кретные реализации
были производными от класса
BaseService
.
Чтобы сделать этот программный код максимально удобным в обслужи-
вании и благоприятным для программиста, мы хотели удостовериться, что
создание экземпляров базового класса невозможно,
упущение из виду реализации методов интерфейса в одном из под-
классов вызывает ошибку на ранней стадии.
Итак, почему же может возникнуть потребность в использовании моду-
ля Python
abc
для решения этой задачи? Названная выше конструкция
довольно распространена в более сложных системах. Чтобы обеспечить
реализацию ряда методов базового класса производным классом, как
правило, используется примерно такая идиома Python:
class Base:
def foo(self):
raise
NotImplementedError()
def bar(self):
raise
NotImplementedError()
class Concrete(Base):
def foo(self):
return 'вызвана foo()'
# О нет, мы забыли переопределить bar()...
# def bar(self):
# return "вызвана bar()"
Итак, что же мы получаем из этой первой попытки решения задачи?
Вызов
методов экземпляра
Base
правильно вызывает исключения
NotImplementedError
:
>>> b = Base()
>>> b.foo()
NotImplementedError
4 .5 . Абстрактные базовые классы держат
наследование под контролем 127
Более того, и создание экземпляра, и использование
Concrete
работают
так, как ожидалось. И если вызвать не реализованный в нем метод, такой
как
bar()
, то в результате тоже будет вызвано исключение:
>>> c = Concrete()
>>> c.foo()
'вызвана foo()'
>>> c.bar()
NotImplementedError
Эта первая реализация выглядит неплохо, но пока не идеально. Ее обо-
ротными сторонами является то,
что мы по-прежнему можем
легко создавать экземпляры
Base
, не
получая ошибку, а также
обеспечивать неполные подклассы — создание экземпляра
Concrete
не
будет вызывать ошибку до тех пор, пока мы не вызовем отсутствующий
метод
bar()
.
При помощи модуля Python
abc
, который был добавлен в Python 2.6
1
, мы
можем добиться большего успеха и решить эти оставшиеся проблемы. Вот
обновленная реализация с использованием абстрактного класса, опреде-
ленного в
модуле
abc
:
from abc import ABCMeta, abstractmethod
class Base(metaclass=ABCMeta):
@abstractmethod
def foo(self):
pass
@abstractmethod
def bar(self):
pass
class Concrete(Base):
def foo(self):
pass
Do'stlaringiz bilan baham: