Паттерн “Стратегия” позволяет выбирать тип алгоритма, который реализует общий интерфейс, прямо во время работы приложения.
Данный паттерн относится к поведенческим шаблонам проектирования.
Допустим мы разрабатываем музыкальный плеер со встроенными кодеками. Под встроенными кодеками подразумеваяется чтение музыкальных форматов без использования внешних источников операционной системы (кодеков), плеер должен уметь читать треки разных форматов и воспроизводить их. Такими возможностями обладает плеер VLC, он поддерживает разные типы видео и аудио форматов, запускается на популярных и не очень операционных системах.
Представим как выглядит наивная имплементация плеера:
var player: MusicPlayer? func play(filePath: String) { let extension = filePath.pathExtension if extension == "mp3" { playMp3(filePath) } else if extension == "ogg" { playOgg(filePath) } } func playMp3(_ filePath: String) { player = MpegPlayer() player?.playMp3(filePath) } func playOgg(_ filePath: String) { player = VorbisPlayer() player?.playMusic(filePath) }
Далее мы добавляем несколько форматов, что приводит к необходимости написания дополнительных методов. Плюс плеер обязан поддерживать подключаемые библиотеки, с новыми форматами аудио, которые будут появляться в последствии. Налицо существует потребность в переключении алгоритма проигрывания музыки, для решения этой задачи используется паттерн Стратегия.
Создадим общий протокол MusicPlayerCodecAlgorithm, напишем реализацию протокола в двух классах MpegMusicPlayerCodecAlgorithm и VorbisMusicPlayerCodecAlgorithm, для проигрывания mp3 и ogg файлов со-но. Создадим класс MusicPlayer, который будет содержать референс на алгоритм который необходимо переключать, далее по расширению файла реализуем переключение типа кодека:
import Foundation class MusicPlayer { var playerCodecAlgorithm: MusicPlayerCodecAlgorithm? func play(_ filePath: String) { playerCodecAlgorithm?.play(filePath) } } protocol MusicPlayerCodecAlgorithm { func play(_ filePath: String) } class MpegMusicPlayerCodecAlgorithm: MusicPlayerCodecAlgorithm { func play(_ filePath: String) { debugPrint("mpeg codec - play") } } class VorbisMusicPlayerCodecAlgorithm: MusicPlayerCodecAlgorithm { func play(_ filePath: String) { debugPrint("vorbis codec - play") } } func play(fileAtPath path: String) { guard let url = URL(string: path) else { return } let fileExtension = url.pathExtension let musicPlayer = MusicPlayer() var playerCodecAlgorithm: MusicPlayerCodecAlgorithm? if fileExtension == "mp3" { playerCodecAlgorithm = MpegMusicPlayerCodecAlgorithm() } else if fileExtension == "ogg" { playerCodecAlgorithm = VorbisMusicPlayerCodecAlgorithm() } musicPlayer.playerCodecAlgorithm = playerCodecAlgorithm musicPlayer.playerCodecAlgorithm?.play(path) } play(fileAtPath: "Djentuggah.mp3") play(fileAtPath: "Procrastinallica.ogg")
В приведенном выше примере также показан простейший пример фабрики (переключение типа кодека от расширения файла)
Важно отметить что паттерн Стратегия не создает объекты, только лишь описывает способ создания общего интерфейса для переключения семейства алгоритмов.
Источники
https://refactoring.guru/ru/design-patterns/strategy
Исходный код
https://gitlab.com/demensdeum/patterns/