В программировании много ситуаций, когда одна и та же задача может требовать разные подходы в зависимости от условий. Представим, что мы разрабатываем систему для расчета стоимости туристической страховки. Стоимость такой страховки может варьироваться в зависимости от множества факторов. А формула расчета может меняться в зависимости от этих факторов.
В простейшем случае можно написать большое количество условий и в каждом из них производить расчет. Например:
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 лет. Добавление новой стратегии не требует изменения существующего кода, что делает нашу систему гибкой и расширяемой.
Так с помощью паттерна «Стратегия» мы можем гибко управлять поведением нашего приложения и легко адаптировать его к меняющимся требованиям.
Выводы
Паттерн «Стратегия» следует использовать с умом. Если количество стратегий становится слишком большим, или если они значительно отличаются друг от друга, лучше выбрать другой подход.
В любом случае знание и понимание различных паттернов проектирования является важным навыком для каждого программиста.
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.