Перейти к основному содержимому

Открыт для расширения, закрыт для изменения

Принцип открытости/закрытости гласит: код должен быть открыт для расширения — можно добавлять новое поведение, — но закрыт для изменения — существующий, работающий, протестированный код ради этого не правится.

Каждая правка работающего кода — это шанс его сломать. Когда добавление возможности означает только добавление нового кода, старый код остаётся проверенным.

Инструмент Python для этого — протокол: класс на основе typing.Protocol, который перечисляет методы, обязательные для типа. Любой класс с подходящими методами автоматически удовлетворяет протоколу — ничего объявлять не нужно, — поэтому функцию, зависящую от протокола, никогда не придётся менять, когда подключается новый класс.


match, который постоянно растёт

Этот калькулятор площади использует match по виду фигуры. Он работает — пока не появится следующая фигура. Треугольник? Правим match. Пятиугольник? Правим снова. Функция никогда не бывает законченной, и каждая правка подвергает риску фигуры, которые уже работали.

match по виду фигуры

total_area должна знать каждый вид фигуры. Добавление нового вида означает изменение этой протестированной функции — именно то, от чего предостерегает принцип.

Prompt
Напишите программу на Python с функцией total_area, которая вычисляет суммарную площадь фигур с помощью match по полю kind (круг и прямоугольник хранятся в одном dataclass с width, height, radius). Выведите общую площадь нескольких фигур — как плохую отправную точку для принципа открытости/закрытости.
Вывод: полная программа на Python

Расширение через протокол

Решение: каждая фигура сама знает свою площадь. Протокол Shape требует один метод, area(), а total_area просто складывает всё, что получает, — ей больше не важно, что это за фигуры.

Теперь посмотрите на принцип в действии: подключается совершенно новый Triangle, а total_area не затрагивается. Расширение без изменения.

Каждая фигура вычисляет свою площадь

total_area зависит только от протокола Shape. match исчез — а вместе с ним и необходимость когда-либо снова править эту функцию.

Prompt
Переработайте фигуры на Python в стиле принципа открытости/закрытости: протокол Shape с методом area, отдельные dataclass Circle и Rectangle, каждый из которых вычисляет свою площадь, и функция total_area, которая суммирует любой список фигур без match. Выведите общую площадь.
Вывод: полная программа на Python

Добавляем новую фигуру, ничего не трогая

Triangle — полностью новый код: dataclass и его метод area(). total_area, Circle и Rectangle остались точно такими же, какими были, — открыты для расширения, закрыты для изменения.

Prompt
Расширьте пример с протоколом Shape на Python совершенно новым dataclass Triangle, реализующим area, не изменяя total_area, Circle и Rectangle. Выведите новую общую площадь с учётом треугольника, чтобы показать расширение без изменения.
Вывод: полная программа на Python
Add a new shape, touch nothing