Преимущества использования шаблона стратегии в php

Я не могу понять, какие преимущества предлагает шаблон стратегии. См. Пример ниже.

//Implementation without the strategy pattern
class Registry {

    public function Func1(){
         echo 'called function 1';
    }

    public function Func2(){
         echo 'called function 2';
    }

}

$client = new Registry();
$client->Func1();
$client->Func2();

//Implementation with strategy pattern
interface registry {
     public function printMsg();
}

class Func1 implements registry {
     public function printMsg(){
         echo 'called function 1';
    }
}

class Func2 implements registry {
     public function printMsg(){
         echo 'called function 2';
    }
}

class context {

      public function printMsg(Registry $class){
          $class->printMsg();
      }
}

$client = new context();
$client->printMsg(new Func1());
$client->printMsg(new Func2());

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

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

Ответ 1

Цель шаблона стратегии:

Определите семейство алгоритмов, инкапсулируйте каждый из них и сделайте их взаимозаменяемыми. Стратегия позволяет алгоритму независимо варьироваться от клиентов, которые его используют. [GoF: 349]

Чтобы понять, что это значит, вы должны (акцент мой)

Рассмотрим, что должно быть переменным в вашем дизайне. Этот подход является противоположностью сосредоточению внимания на причине редизайна. Вместо того, чтобы рассматривать, что может повлиять на изменение дизайна, рассмотрите, что вы хотите изменить без редизайна. Здесь основное внимание уделяется инкапсуляции концепции, которая меняется, тема многих шаблонов проектирования. [GoF: 29]

Другими словами, стратегии связаны с фрагментами кода, которые вы можете подключить к клиенту (другому объекту) во время выполнения, чтобы изменить его поведение. Одна из причин этого - предотвратить вас прикосновение к клиенту при каждом добавлении нового поведения (см. Открытый закрытый принцип (OCP) и Protected Variation). Кроме того, когда вы получаете достаточно сложные алгоритмы, помещая их в свои классы, помогает придерживаться принципа единой ответственности (SRP).

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

Но так или иначе, в вашем первом коде Registry реализует методы Func1 и Func2. Поскольку мы предполагаем, что это связанные алгоритмы, пусть притворяются, что они действительно являются persistToDatabase() и persistToCsv(), чтобы что-то обернуть вокруг нас. Предположим также, что в конце запроса приложения вы вызываете один из этих методов из обработчик выключения (клиент).

Но какой метод? Ну, это зависит от того, что вы настроили, и флаг для этого явно хранится в самом реестре. Таким образом, у вашего клиента вы получите что-то вроде

switch ($registry->persistStrategy)
{
    case 'database':
        $registry->persistToDatabase();
    case 'csv':
        $registry->persistToCsv();
    default:
        // do not persist the database
}

Но операторы switch, подобные этому, являются плохими (см. CleanCode: 37ff). Представьте, что ваш клиент просит вас добавить метод persistToXml(). Теперь вы не только должны изменить свой класс реестра, чтобы добавить другой метод, но также вам нужно изменить клиент для размещения этой новой функции. Это два класса, которые вы должны изменить, когда OCP сообщает нам, что наши классы должны быть закрыты для модификации.

Одним из способов улучшить это было бы добавление общего регрессионного метода persist() в реестр и перемещение в него переключателя/случая, поэтому клиенту нужно только позвонить

$registry->persist();

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

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

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

class Registry
{
    public function persist()
    {
        $this->persistable->persist($this->data);
    }
    public function setPersistable(Persistable $persistable)
    {
        $this->persistable = $persistable
    }
    // other code …

Приятно, мы реорганизовали условное с полиморфизмом. Теперь вы и все остальные разработчики можете установить любую Persistable как желаемую стратегию:

$registry->setPersistable(new PersistToCloudStorage);

И что это. Больше нет переключателя/корпуса. Больше нет взлома реестра. Просто создайте новый класс и установите его. Стратегия позволяет алгоритму независимо варьироваться от клиентов, которые его используют.

Также см.

Примечания к концу:

[GoF] Gamma, E., Helm, R., Johnson, R., Vlissides, J., Design Patterns: Элементы многоразового ObjectOriented Software, Reading, Mass.: AddisonWesley, 1995.

[CleanCode] Мартин, Роберт С. Чистый код: руководство по гибкому программному мастерству. Верхняя река седла, Нью-Джерси: Prentice Hall, 2009. Печать.

Ответ 2

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

class context {

      public function printMsg(registry $class){
          $class->printMsg();
      }
}

С именем интерфейса типа hinting. Чтобы быть более ясным, позвольте мне показать вам небольшой пример. Представьте, что у вас есть Iphone и Android Какова их общая точка зрения? Один - это оба телефона. вы можете создать такой интерфейс

interface Telephone {
    public function enterNumber();
    public function call();
    public function sentTextMessage();
}

и реализовать интерфейс в каждом из ваших телефонов:

class Iphone implements Telephone {
     // implement interface methods
}

class Android implement Telephone {

}

и вы можете иметь сервис, подключенный ко всем телефонам, таким как GPS на вашем автомобиле: и убедитесь, что если вы подключите телефон (обычно с интерфейсом BlueTooth). вы можете сделать что-то вроде этого:

class carGps{
   public function handFreeCall(Telephone $tel){
       $this->amplifyVolume($tel->call());
   }
}


$tomtom = new CarGps();
$tomtom->handFreeCall(new Iphone());
//or if you like to:
$tomtom->handFreeCall(new Android());

как разработчик приложения, вы сможете использовать свой handFreeCall на всех телефонах, которые реализуют интерфейс телефона, не нарушая ваш код, потому что вы будете знать, что телефон способен звонить.

Надеюсь, я помог.

Ответ 3

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

В классе, в котором вы создаете экземпляр класса, состоящего из стратегии, вы создаете экземпляр класса стратегии, передавая его конструктору составленного класса.

Таким образом, мы программируем интерфейс, а не реализуем, и поэтому в любой момент времени мы можем добавить больше классов в иерархию класса стратегии и передать их классу, составленному из стратегии

Просьба прочесть следующую ссылку

Ответ 5

Несколько важных особенностей шаблона Стратегии, как предусмотрено Группой Четырех, являются:

  • У него нет условных операторов (так что оставьте свой переключатель, если и т.д.)
  • У него есть участник контекста

Большинство рекомендаций по стратегии не учитывают участника контекста. Здесь вы можете найти пять различных примеров шаблона стратегии: http://www.php5dp.com/category/design-patterns/strategy/

Ответ 6

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

1. Интерфейс для методов, которые различаются (например, формат предупреждения в случае):

require_once("initialize/initialize.php");
interface alert{
public function alert($message);
};

2. Методы, реализующие интерфейс оповещений.

class alertBorderBullet implements alert{
public function alert($message){
$alert = "<p style='border:1px solid #eee; padding:4px; padding-left:8px; padding-right:8px; border-left:4px solid #FC0; margin-top:8px; margin-bottom:8px; color:#888'>".$message."</p>";
return $alert;
}
};

class alertOrangeBgnd implements alert{
public function alert($message){
$alert = "<p style='color:#fff; background-color:#ff9c3a; padding:4px; padding-left:8px; padding-right:8px; margin-top:8px; margin-bottom:8px; border-left:4px solid #e471bd;'>".$message."</p>";
return $alert;
}
};

class alertRed implements alert{
public function alert($message){
$alert = "<p style='color:#c11; background-color:#efefef; padding:4px; padding-left:12px; padding-right:8px; margin-top:8px; margin-bottom:8px;'>".$message."</p>";
return $alert;
}
};

3. Messenger, чтобы разделить настройку метода оповещения и получить от других объектов в проекте.

class alertMessenger{
protected $_alert;
public function setAlertType(alert $alert){$this->_alert = $alert;}
public function returnAlert($message){return $this->_alert->alert($message);}
};

4. Случайный объект проекта, который будет использовать "оповещение" по-разному.

class randomObject{
public $alert;
public function __construct(){
    $this->alert = new alertMessenger;
}
// More code here...
};

$randomObject = new randomObject;
$randomObject->alert->setAlertType(new alertRed);
echo $randomObject->alert->returnAlert($message="Red text for critical info");
$randomObject->alert->setAlertType(new alertBorderBullet);
echo $randomObject->alert->returnAlert($message="Border, bullet and pale-color text");
echo $randomObject->alert->returnAlert($message="Repeat, check style permanence");
$randomObject->alert->setAlertType(new alertOrangeBgnd);
echo $randomObject->alert->returnAlert($message="Again, another redefined message style");

randomObject, когда инициализировано (для этого случая) автоматически создает экземпляр alertMessanger и делает доступными его методы. Поведение может быть установлено, а сообщения эхом. Другие форматы оповещений могут быть созданы и использованы при необходимости с помощью setAlertType, а затем returnAlert.