Я не могу понять, какие преимущества предлагает шаблон стратегии. См. Пример ниже.
//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());
В приведенных выше двух примерах, какие преимущества будет предлагать стратегия, и как это лучше, чем первый подход? Почему я должен использовать шаблон стратегии?
Вышеприведенный пример кода может содержать ошибки, пожалуйста, игнорируйте код.
Целью стратегии является:
Определите семейство алгоритмов, инкапсулируйте их и сделайте их взаимозаменяемыми. Стратегия позволяет алгоритму независимо варьироваться от клиентов, которые его используют. [GoF: 349]
Чтобы понять, что это значит, вы должны (акцент мой)
Подумайте, что должно быть переменным в вашем дизайне. Этот подход является противоположностью сосредоточению внимания на причине редизайна. Вместо того, чтобы рассматривать то, что может повлиять на изменение дизайна, рассмотрите, что вы хотите изменить без редизайна . Здесь основное внимание уделяется инкапсуляции концепции, которая меняется, тема многих шаблонов проектирования. [GoF: 29]
Другими словами, стратегии связаны с фрагментами кода, которые вы можете подключить к клиенту (другому объекту) во время выполнения, чтобы изменить его поведение. Одна из причин этого – предотвратить вас прикосновение к клиенту при каждом добавлении нового поведения (см. « Открытый закрытый принцип» (OCP) и « Защищенная вариация» ). Кроме того, когда вы получаете достаточно сложные алгоритмы, вкладывая их в свои классы, помогает придерживаться принципа единой ответственности (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()
в реестр и перенос переключателя / case в него, поэтому клиенту нужно только позвонить
$registry->persist();
Это лучше, но все равно оставляет нас с коммутатором / футляром, и он по-прежнему заставляет нас модифицировать реестр каждый раз, когда мы добавляем новый способ его сохранения.
Теперь также представьте, что ваш продукт является основой, используемой многими разработчиками, и они придумывают свои собственные алгоритмы 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. Печать.
В основном стратегия предназначена для группировки функциональности для нескольких классов. О, и у вас есть опечатка в вашем коде
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());
-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 на всех телефонах, которые реализуют интерфейс телефона, не нарушая ваш код, потому что вы узнаете, что телефон способен звонить.
Надеюсь, я помог.
Шаблон стратегии помогает абстрагироваться от алгоритмического подхода для выполнения определенной работы. Предположим, вы хотите отсортировать массив чисел с помощью разных алгоритмов. В этом случае лучше применить шаблон стратегии, чем плотно связать алгоритм с вашим кодом.
В классе, в котором вы создаете класс, составленный из стратегии, вы создаете ссылку на класс стратегии, передавая ее конструктору составленного класса.
Таким образом, мы программируем интерфейс, а не реализуем, и поэтому в любой момент времени мы можем добавить больше классов в иерархию стратегического класса и передать их классу, составленному из стратегии
Просьба проследить следующую ссылку
Здесь есть библиотека и набор symfony:
Пара важных особенностей шаблона Стратегии, как это предусмотрено «Бандой четырех»:
Большинство рекомендаций по Стратегии не учитывают участника Контекста. Вы можете найти пять различных примеров шаблона стратегии в PHP здесь: http://www.php5dp.com/category/design-patterns/strategy/
Часто примеры несколько странные, описывающие уток, кошек или других. Вот пример шаблона стратегии, используемый при отображении предупреждений. (Расширение ответа Гордона).
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.