До сих пор мы рассмотрели три шаблона дизайна в этой серии. Мы определили четыре категории различных шаблонов дизайна. В этой статье я собираюсь объяснить шаблон проектирования стратегии , который относится к шаблонам поведенческого дизайна .
У вас может возникнуть вопрос: когда мы должны использовать этот шаблон проектирования? Я бы сказал, когда у нас есть несколько способов (алгоритмов) для выполнения одной и той же операции, и мы хотим, чтобы приложение выбирало конкретный путь на основе имеющихся у вас параметров. Этот шаблон также известен как шаблон политики.
Очень простым примером для этой статьи будет функция сортировки. Например, у нас есть несколько алгоритмов сортировки массивов, но исходя из количества элементов массива, мы должны выбрать, какой алгоритм использовать, который дает наилучшую производительность.
Этот шаблон также известен как шаблон политики.
Проблема
Я возьму пример веб-сайта электронной коммерции с несколькими интегрированными платежными шлюзами. Хотя на сайте имеется несколько платежных шлюзов, в соответствии с требованиями они не все будут отображаться в интерфейсе. Вместо этого необходимо выбрать подходящий платежный шлюз на лету на основе суммы корзины.
Если использовать простой пример, то, если стоимость корзины меньше 500 долларов США, платеж должен обрабатываться с использованием стандарта PayPal, но если он составляет 500 долларов США или более, он должен обрабатываться с использованием сохраненных данных кредитной карты (при условии, что данные уже сохранены). ).
Без реализации правильной стратегии наш код будет выглядеть следующим образом:
Во-первых, у нас будут основные классы как для оплаты через Paypal, так и для оплаты с помощью кредитной карты, которые приведены ниже.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
// Class to pay using Credit Card
class payByCC {
private $ccNum = »;
private $ccType = »;
private $cvvNum = »;
private $ccExpMonth = »;
private $ccExpYear = »;
public function pay($amount = 0) {
echo «Paying «.
}
}
// Class to pay using PayPal
class payByPayPal {
private $payPalEmail = »;
public function pay($amount = 0) {
echo «Paying «.
}
}
// This code needs to be repeated every place where ever needed.
$amount = 5000;
if($amount >= 500) {
$pay = new payByCC();
$pay->pay($amount);
} else {
$pay = new payByPayPal();
$pay->pay($amount);
}
|
Здесь вы можете сказать, что нам нужно разместить условные операторы, чтобы наш код работал. Представьте себе количество изменений, которые необходимо внести, когда нам потребуются новые изменения в этой логике или вы обнаружили ошибку в этой логике. Нам нужно будет добавить патч для всех тех мест, где мы использовали этот код.
Решение
Мы реализуем то же требование, но с использованием шаблона «Стратегия», который позволяет нам сделать наш код более понятным, понятным и расширяемым.
Интерфейс
Сначала мы реализуем интерфейс, который будет использоваться всеми различными классами платежных шлюзов. В конечном счете, это наши стратегии.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
interface payStrategy {
public function pay($amount);
}
class payByCC implements payStrategy {
private $ccNum = »;
private $ccType = »;
private $cvvNum = »;
private $ccExpMonth = »;
private $ccExpYear = »;
public function pay($amount = 0) {
echo «Paying «.
}
}
class payByPayPal implements payStrategy {
private $payPalEmail = »;
public function pay($amount = 0) {
echo «Paying «.
}
}
|
Далее мы создадим наш основной класс, который может использовать другую стратегию, чем мы реализовывали до сих пор.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class shoppingCart {
public $amount = 0;
public function __construct($amount = 0) {
$this->amount = $amount;
}
public function getAmount() {
return $this->amount;
}
public function setAmount($amount = 0) {
$this->amount = $amount;
}
public function payAmount() {
if($this->amount >= 500) {
$payment = new payByCC();
} else {
$payment = new payByPayPal();
}
$payment->pay($this->amount);
}
}
|
Здесь мы видим, что наша условная загрузка способов оплаты осуществляется методом payAmount . Давайте обернем все вместе и посмотрим, как мы можем использовать это дальше.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
interface payStrategy {
public function pay($amount);
}
class payByCC implements payStrategy {
private $ccNum = »;
private $ccType = »;
private $cvvNum = »;
private $ccExpMonth = »;
private $ccExpYear = »;
public function pay($amount = 0) {
echo «Paying «.
}
}
class payByPayPal implements payStrategy {
private $payPalEmail = »;
public function pay($amount = 0) {
echo «Paying «.
}
}
class shoppingCart {
public $amount = 0;
public function __construct($amount = 0) {
$this->amount = $amount;
}
public function getAmount() {
return $this->amount;
}
public function setAmount($amount = 0) {
$this->amount = $amount;
}
public function payAmount() {
if($this->amount >= 500) {
$payment = new payByCC();
} else {
$payment = new payByPayPal();
}
$payment->pay($this->amount);
}
}
$cart = new shoppingCart(499);
$cart->payAmount();
// Output
Paying 499 using PayPal
$cart = new shoppingCart(501);
$cart->payAmount();
//Output
Paying 501 using Credit Card
|
Мы видим, что переворот платежного шлюза не прозрачен для приложения. На основе параметров у него есть подходящий платежный шлюз, доступный для обработки транзакции.
Добавление новой стратегии
Если на более позднем этапе пользователю потребуется добавить новую стратегию (новый платежный шлюз здесь) с другой логикой, в этом случае это будет очень просто. Допустим, мы хотим добавить новый платежный шлюз, Moneybooker, и хотим обрабатывать деньги, когда сумма корзины превышает 500 долларов, но меньше 1000 долларов.
Все, что нам нужно сделать, это создать новый класс стратегии, который реализует наш интерфейс, и мы готовы к работе.
|
1
2
3
4
5
6
7
8
9
|
class payByMB implements payStrategy {
private $mbEmail = »;
public function pay($amount = 0) {
echo «Paying «.
}
}
|
У нас есть готовый новый класс стратегии, и нам нужно изменить наш основной метод payAmount . Это должно быть изменено, как показано ниже:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
public function payAmount() {
if($this->amount > 500 && $this->amount < 1000) {
$payment = new payByMB();
} else if($this->amount >= 500) {
$payment = new payByCC();
} else {
$payment = new payByPayPal();
}
$payment->pay($this->amount);
}
|
Здесь вы можете видеть, что мы внесли изменения только в наш метод payAmount , а не в код клиента, из которого вызывается этот метод.
Вывод
Отсюда следует заключить, что когда у нас есть несколько способов выполнить одну и ту же задачу (на языке программного обеспечения, когда у нас есть несколько алгоритмов для выполнения одной и той же операции), мы должны рассмотреть возможность реализации шаблона стратегии.
Используя этот шаблон, мы можем свободно добавлять / удалять алгоритм, поскольку переключение этих алгоритмов не является прозрачным для приложения.
Я старался изо всех сил предоставить простейший и вместе с тем полезный пример для демонстрации шаблона разработки стратегии, но если у вас есть дополнительные комментарии или вопросы, не стесняйтесь добавлять их в канал ниже.