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

Что такое шаблон стратегии?

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

Попробуем провести аналогию. Допустим, вы хотите оценить пару действительно сложных математических уравнений. Разве не было бы совершенно раздражающим (и совсем не экологически безопасным) приобретение четырех разных калькуляторов для решения задач, включающих все четыре основных арифметических действия? Я думаю, вы согласитесь, что было бы намного лучше иметь один калькулятор, который мог бы переключаться между различными арифметическими операциями в зависимости от того, что нужно для решения задачи. Это именно та основа, на которой был построен Паттерн Стратегии.

Давайте углубимся в код

Для этого примера кода я решил создать калькулятор расстояния. Цель состоит в том, чтобы вычислить расстояние между двумя точками. Вычисление расстояния между двумя точками обычно выполняется с использованием евклидовой метрики. Но на самом деле существует несколько способов вычисления расстояния между двумя точками, в зависимости от того, как выглядит пространство между ними.

Мы не будем усложнять задачу и просто добавим Манхэттенское расстояние (которое иногда называют Змеиное расстояние). , Расстояние до городских кварталов или Расстояние по прямой. Манхэттенское расстояние используется при расчете расстояния в так называемом >Геометрия такси. Я не буду вдаваться в подробности, но те, кому интересно, могут прочитать больше о геометрии такси прямо здесь. при попытке реализации ниже:

В этой попытке мы создаем перечисление, содержащее несколько статических функций, которые мы можем использовать (мы используем перечисление без учета регистра, потому что его нельзя создать). Каждая функция дает нам доступ к разному способу вычисления расстояния между двумя точками, и все хорошо… почти.

Главный недостаток этого подхода заключается в том, что если мы хотим добавить функцию для вычисления расстояния Минковского (это странно, он был странным парнем), нам пришлось бы добавить новую функцию с тем же именем, что и другие, а затем мы пришлось бы изменить все вызовы функций, где мы хотим рассчитать новое расстояние… такой труд.

Приходит шаблон стратегии

Что, если мы решим подойти к этой проблеме с помощью шаблона стратегии? Взгляните на реализацию ниже:

Давайте рассмотрим, что здесь происходит.
Мы начнем с определения именованного протокола, который мы будем использовать в качестве абстракции, чтобы нам не пришлось менять весь наш код, если мы изменим конкретные типы, обеспечивающие стратегии. Затем мы создаем перечисление с именем DistanceStrategyType с двумя случаями, которые соответствуют нашим стратегиям расчета расстояния, .euclidian и .manhattan. Затем внутри перечисления мы предоставляем реализацию вычисляемого свойства distance, которое мы объявили в протоколе DistanceStrategy, и заставляем его возвращать разные уравнения для вычисления двух расстояний. Третье, что мы делаем, это создаем наш фактический файл DistanceCalculator. Я решил сделать его структурой для этой статьи, вы можете сделать его классом, это немного зависит от того, что вам нужно. DistanceCalculator хранит переменную стратегии, которая должна соответствовать протоколу DistanceStrategy, но это почти все, что мы о нем знаем… на самом деле, это все, что нам нужно знать. С его помощью мы реализуем метод .distance(_:_:) -> CGFloat, который просто вычисляет свойство расстояния стратегии и вызывает его с двумя полученными точками.

В этом есть два огромных преимущества.

  1. Мы можем легко добавить больше случаев вычисления расстояния, не меняя полностью остальную часть нашего кода. Просто введите новую стратегию в свой калькулятор и БАМ… готово!
  2. Мы можем изменить DistanceStrategyType на структуру или класс, или переименовать его, или реализовать массу других методов, но остальному нашему коду все равно. Его заботит только то, чтобы новый тип, который мы создаем, соответствовал протоколу DistanceStrategy.

На этот раз все! Не стесняйтесь комментировать, если у вас есть вопросы, и подпишитесь, чтобы получать уведомления о будущих статьях.

Чтобы узнать больше о разработке программного обеспечения, ознакомьтесь с моими предыдущими статьями: