Мобильная
версия

Паттерн Стратегия

Дата: Категория: Паттерны проектирования

В продолжении темы о паттернах, сегодня я расскажу вам простым языком о паттерне "Стратегия".

"поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости."
(с) Wikipedia

Ничего удивительного - как обычно все слишком туманно.
Попробуем разобраться.

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

Как это можно было бы сделать?
Вполне легко и просто, например так.

class cat{
    function getShoes(){} // принести тапочки
    function jump(){} // подпрыгнуть
}
class dog{
    function getShoes(){} // принести тапочки
    function jump(){} // подпрыгнуть
}

чтож, очень даже неплохо, и даже с первого взгляда не представялется какие могут быть тут впоследствии проблемы.
А если нам понадобится добавить еще одно животное (корову) и добавить еще одно умение (подавать голос по команде), то наши классы очень даже разрастутся. А если животных будет 100, и еще 100 команд? А если у одной из команд нам не понравится название и нужно будет ее изменить? Правильно - в таком случае у нас будет крайне много нудной и скучной работы. Этого надо избежать. Например мы можем выделить в класс все эти методы и наследовать потом всех животных уже от него.

Попробуем

class animal{
    function getShoes(){} // принести тапочки
    function jump(){} // подпрыгнуть
}
class cat extended animal{}
class dog extended animal{}

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

Вернемся к нашей корове - вы видели чтобы корова прыгала? 
Я, конечно, не рос на фермах, и если честно то не представляю как корова может прыгать (да простят меня те, кто близко знаком с коровам если я не прав), и получается что у коровы не должно быть метода jump. Вот не должно быть и все. Как тогда быть?

А вот теперь к нам на помощь приходит паттерн стратегия. 
Что он из себя представляет?

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

Для начала вспомним о паттерне Абстрактная фабрика и создадим абстрактные классы для животных и для их умений

class animal{} // животные
abstract class skill{} // умения

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

class animal{
    // умение животного
    protected $skill = null; 
    // добавляем к животному умение
    function setSkill(skill $skill){
        $this->skill = $skill;
    }
    // делаем то что он умеет
    function _do(){
        // если вообще умеет
        if(!$this->skill){
            return;
        }
        $this->skill->_do();
    }
    // обертка, позволяющая выполнять команду сразу после ее задания
    // по сути совмещение двух верхних команд
    function doSkill(skill $skill){
        $this->skill = $skill;
        $this->skill->_do();
    }
}
abstract class skill{
    abstract function _do();
}

Как видите у нас сейчас получилась практически абстрактная фабрика (только без создания класса)
Любому животному мы можем задать в качестве умения класс наследник от skill, и после выполнить его (метод do)
В том что метод присутствует в классе мы уверены, так как skill - класс абстрактный и все его наследники реализуют метод do

Попробуем реализовать что-нибудь на основе наших классов.

class getShoes extends skill{
    function _do(){
        echo 'Вот ваши тапочки, хозяин';
    }
}
class dog extends animal{}

$dog = new dog;
$dog->setSkill(new getShoes);
$dog->_do(); // вывод: Вот ваши тапочки, хозяин
$dog->doSkill(new getShoes); // вывод: Вот ваши тапочки, хозяин 

Или например

class getShoes extends skill{
    function _do(){
        echo 'Вот ваши тапочки, хозяин';
    }
}
class jump extends skill{
    function _do(){
        echo 'Смотрите, я прыгаю';
    }
}
class dog extends animal{}
class cat extends animal{}

$dog = new dog;
$dog->setSkill(new getShoes);
$dog->_do(); // вывод: Вот ваши тапочки, хозяин
$dog->doSkill(new jump); // вывод: Смотрите, я прыгаю

$cat = new cat;
$cat->doSkill(new jump); // вывод: Смотрите, я прыгаю
$cat->_do(); // вывод: Смотрите, я прыгаю

Как видите - ничего сложного в этом нет. С помощью этого несложного паттерна вы сможете комбинировать что угодно и как угодно.

Реальный пример:
Возьмем опять же формы на сайте. Для формы входа пользователей нужно только поле логин и пароль, а для авторизации, например, еще e-mail и аватарка.
Формы мы используем в качетсве базовых классов (в нашем примере животные) и привязываем к ним различные поля (в нашем примере команды котоыре животные выполняют)

И, напоследок, весь код приведенный в данном примере.

class animal{
    // умение животного
    protected $skill = null;
    // добавляем к животному умение
    function setSkill(skill $skill){
        $this->skill = $skill;
    }
    // делаем то что он умеет
    function _do(){
        // если вообще умеет
        if(!$this->skill){
            return;
        }
        $this->skill->_do();
    }
    // обертка, позволяющая выполнять команду сразу после ее задания
    // по сути совмещение двух верхних команд
    function doSkill(skill $skill){
        $this->skill = $skill;
        $this->skill->_do();
    }
}
abstract class skill{
    abstract function _do();
}

class getShoes extends skill{
    function _do(){
        echo 'Вот ваши тапочки, хозяин';
    }
}
class jump extends skill{
    function _do(){
        echo 'Смотрите, я прыгаю';
    }
}
class dog extends animal{}
class cat extends animal{}

$dog = new dog;
$dog->setSkill(new getShoes);
$dog->_do(); // вывод: Вот ваши тапочки, хозяин
$dog->doSkill(new jump); // вывод: Смотрите, я прыгаю

$cat = new cat;
$cat->doSkill(new jump); // вывод: Смотрите, я прыгаю
$cat->_do(); // вывод: Смотрите, я прыгаю

Теги: #Паттерны проектирования, #ООП, # Стратегия

Ваша оценка:

Рейтинг: 9.9 (Оценок: 4)

Комментарий:

Copyright © DOC_tr 2015-2017 г. Все права защищены
Яндекс.Метрика
Перейти к мобильной версии