Статьи

Шаблоны проектирования: шаблон стратегии

До сих пор мы рассмотрели три шаблона дизайна в этой серии. Мы определили четыре категории различных шаблонов дизайна. В этой статье я собираюсь объяснить шаблон проектирования стратегии , который относится к шаблонам поведенческого дизайна .

У вас может возникнуть вопрос: когда мы должны использовать этот шаблон проектирования? Я бы сказал, когда у нас есть несколько способов (алгоритмов) для выполнения одной и той же операции, и мы хотим, чтобы приложение выбирало конкретный путь на основе имеющихся у вас параметров. Этот шаблон также известен как шаблон политики.

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

Этот шаблон также известен как шаблон политики.

Я возьму пример веб-сайта электронной коммерции с несколькими интегрированными платежными шлюзами. Хотя на сайте имеется несколько платежных шлюзов, в соответствии с требованиями они не все будут отображаться в интерфейсе. Вместо этого необходимо выбрать подходящий платежный шлюз на лету на основе суммы корзины.

Если использовать простой пример, то, если стоимость корзины меньше 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 , а не в код клиента, из которого вызывается этот метод.

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

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

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