Зарегистрируйтесь, чтобы продолжить обучение

Стратегия (Паттерн) Python: Полиморфизм

В программировании много ситуаций, когда одна и та же задача может требовать разные подходы в зависимости от условий. Представим, что мы разрабатываем систему для расчета стоимости туристической страховки. Стоимость такой страховки может варьироваться в зависимости от множества факторов. А формула расчета может меняться в зависимости от этих факторов.

В простейшем случае можно написать большое количество условий и в каждом из них производить расчет. Например:

if age < 18:
    cost = salary * age
    if country == 'uganda':
        cost = cost / 2
elif 18 <= age < 24:
    # и так далее

Но с течением времени, когда количество условий и сценариев начинает расти, этот код становится все более запутанным и сложным для понимания.

Представим, что нам придется удерживать все эти условия и сценарии в голове в то время, как мы пытаемся понять и обслуживать код. Это может стать непосильной задачей.

Этот код можно упростить и сделать его более понятным и удобным для работы с помощью паттерна проектирования «Стратегия». Его мы и разберем в этом уроке.

Как работает паттерн «Стратегия»

Стратегия — этот паттерн, который применяется для выбора алгоритма поведения в зависимости от состояния системы.

В качестве примера мы рассмотрим задачу расчета стоимости туристической страховки. Стоимость такой страховки может зависеть от множества факторов. И не всегда эти факторы влияют только на численные значения в формуле расчета, иногда они могут менять и саму формулу.

В таких случаях удобно применять паттерн «Стратегия». Он позволяет выбирать наиболее подходящий алгоритм в зависимости от текущих условий. В нашем случае это может быть выбор различных способов расчета стоимости страховки.

Разберем применение этого паттерна по шагам:

  • Определим стратегии, которые реализуют этот интерфейс. Допустим, у нас есть две стратегии: базовая страховка и страховка для активного отдыха:
class BasicInsuranceStrategy:
    def calculate(self, data: dict) -> float:
        return data["days"] * 100


class ActiveRestInsuranceStrategy:
    def calculate(self, data: dict) -> float:
        return data["days"] * 200 + data["risk_activities"] * 50

Каждый из классов стратегий (BasicInsuranceStrategy и ActiveRestInsuranceStrategy) реализует метод calculate(), который выполняет расчет стоимости страховки в соответствии с определенными формулами.

Формула расчета базовой страховки: количество дней умножаем на 100. При этом страховка для активного отдыха включает дополнительные рисковые активности в свою формулу расчета.

  • Создадим класс InsuranceCalculator, который будет использовать одну из этих стратегий для расчета стоимости страховки:
class InsuranceCalculator:
    def __init__(self, strategy):
        self.strategy = strategy

    def calculate(self, data: dict) -> float:
        return self.strategy.calculate(data)

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

  • Выбираем подходящую стратегию и используем ее для расчета стоимости страховки:
basic_strategy = BasicInsuranceStrategy()
active_rest_strategy = ActiveRestInsuranceStrategy()

calculator = InsuranceCalculator(basic_strategy)
params = {"days": 10}
print(calculator.calculate(params))  # 1000

calculator = InsuranceCalculator(active_rest_strategy)
params = {"days": 10, "risk_activities": 5}
print(calculator.calculate(params))  # 2250

Мы можем легко изменить стратегию расчета стоимости страховки. Вначале мы используем базовую стратегию, затем меняем ее на стратегию для активного отдыха. В итоге расчеты осуществляются согласно выбранной стратегии.

Так паттерн «Стратегия» позволяет нам легко добавлять новые способы расчета стоимости страховки, не внося значительных изменений в основной код программы. Например, если у нас появится новый вид страховки для путешественников старше 65 лет, мы просто добавим новую стратегию:

class SeniorInsuranceStrategy:
    def calculate(self, data: dict) -> float:
        return data["days"] * 150


senior_strategy = SeniorInsuranceStrategy()
calculator = InsuranceCalculator(senior_strategy)
params = {"days": 10}

print(calculator.calculate(params))  # 1500

Здесь мы добавили новую стратегию для путешественников старше 65 лет. Добавление новой стратегии не требует изменения существующего кода, что делает нашу систему гибкой и расширяемой.

Так с помощью паттерна «Стратегия» мы можем гибко управлять поведением нашего приложения и легко адаптировать его к меняющимся требованиям.

Выводы

Паттерн «Стратегия» следует использовать с умом. Если количество стратегий становится слишком большим, или если они значительно отличаются друг от друга, лучше выбрать другой подход.

В любом случае знание и понимание различных паттернов проектирования является важным навыком для каждого программиста.


Дополнительные материалы

  1. Что такое expression problem
  2. Expression Problem (англ.)

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff