3.8. Strategy¶
3.8.1. تعریف¶
دیزاین پترن Strategy یک الگوی رفتاری محسوب میشه که اجازه میده استراتژی های مختلفی رو در قالب یک سری آبجکت با interface مشترک تعریف کنیم و با توجه به نیاز برنامه در زمان مورد نظر از هر کدوم استفاده کنیم.
در واقع به شما اجازه میده یک خانواده از الگوریتم ها رو در کلاس های مختلف تعریف کنید و کاری کنید که به راحتی قابل جایگزینی با همدیگه باشن.
3.8.2. چه زمانی استفاده میشه؟¶
این الگوریتم رو زمانی استفاده می کنیم که در برنامه امکان انجام یک عملیات از چند راه مختلف وجود داشته باشه و بخوایم از راحت ترین راه ممکن استراتژی مورد نظر رو انتخاب کنیم.
این الگو رو زمانی استفاده می کنیم که تعداد زیادی کلاس مشابه هم داریم که فقط روش انجام عملیات مشابه شون با هم فرق می کنه.
3.8.3. اجزاء¶
الگوی طراحی Strategy از چند بخش اصلی تشکیل میشه:
یک بخش به نام Context وجود داره که قراره از استراتژی های مختلف استفاده کنه.
یک Strategy Interface داریم که ساختار الگوریتم ها رو مشخص می کنه.
و در نهایت هم انواع استراتژی های پیاده سازی شده رو داریم که می بایست تعریف بشن.
Bocsika, CC0, via Wikimedia Commons
Caution
✅ مزایای استفاده
امکان جا به جایی بین استراتژی ها در زمان اجرا
جداسازی پیاده سازی الگوریتم ها از کدی که از آن استفاده می کند
رعایت اصل Open/Closed: افزودن استراتژی بدون تغییر کد سطح بالا
Warning
❌ معایب استفاده
اگر فقط تعداد محدودی استراتژی دارید که جایگزینی بین اون ها هم کمتر اتفاق میفته، استفاده از این الگو فقط کد رو پیچیده می کنه
بخش Client باید از عملکرد استراتژی مورد نظر مطلع باشه تا بتونه استراتژی درست رو انتخاب کنه
در بسیاری از زبان های برنامه نویسی میتوان به جای این الگو از Anonymous Function ها استفاده کرد
3.8.4. کاربرد عملی¶
این الگو در زبان PHP بسیار استفاده میشه مخصوصا زمانی که نیاز به جایگزینی الگوریتم ها در زمان اجرا با توجه به شرایط مختلف و انتخاب های کاربر باشه.
فرض کنید قصد پیاده سازی سیستم تخفیف یک فروشگاه اینترنتی رو پیاده سازی کنیم.
در این شرایط با توجه به سطح کاربر استراتژی های مختلفی برای محاسبه تخفیف وجود داره.
با توجه به این موضوع میریم سراغ دیزاین پترن Strategy
3.8.5. پیاده سازی¶
قبل از هر چیز Interface مربوط به Strategy ها رو به این صورت داریم:
1<?php
2
3interface DiscountStrategy
4{
5 public function calculateDiscount($price): float;
6}
و بعد استراتژی ها رو با متد مشترک محاسبه ی تخفیف پیاده سازی می کنیم:
1<?php
2
3class SilverDiscount implements DiscountStrategy
4{
5 public function calculateDiscount($price): float
6 {
7 return $price * 0.1; // 10% discount for silver members
8 }
9}
10
11class GoldDiscount implements DiscountStrategy
12{
13 public function calculateDiscount($price): float
14 {
15 return $price * 0.2; // 20% discount for gold members
16 }
17}
18
19class PlatinumDiscount implements DiscountStrategy
20{
21 public function calculateDiscount($price): float
22 {
23 return $price * 0.3; // 30% discount for platinum members
24 }
25}
و بعد هم Context رو داریم که قراره از این استراتژی ها استفاده کنه:
1<?php
2
3class ShoppingCart
4{
5 private DiscountStrategy $discountStrategy;
6 private array $items = [];
7
8 public function addItem(Item $item): void
9 {
10 $this->items[] = $item;
11 }
12
13 public function setDiscountStrategy(DiscountStrategy $discountStrategy): void
14 {
15 $this->discountStrategy = $discountStrategy;
16 }
17
18 public function calculateTotal(): float|int
19 {
20 $subtotal = 0;
21 foreach ($this->items as $item) {
22 $subtotal += $item->price;
23 }
24 $discount = $this->discountStrategy->calculateDiscount($subtotal);
25
26 return $subtotal - $discount;
27 }
28}
3.8.6. نحوه فراخوانی¶
1<?php
2
3class Item
4{
5 public string $name;
6 public float $price;
7
8 public function __construct($name, $price)
9 {
10 $this->name = $name;
11 $this->price = $price;
12 }
13}
14
15// Create a shopping cart
16$cart = new ShoppingCart();
17
18// Add items to the cart
19$item1 = new Item('Widget', 10.00);
20$item2 = new Item('Gadget', 5.00);
21$cart->addItem($item1);
22$cart->addItem($item2);
23
24// Calculate discounts based on customer's membership level
25if ($customer->isSilver()) {
26 $cart->setDiscountStrategy(new SilverDiscount());
27} elseif ($customer->isGold()) {
28 $cart->setDiscountStrategy(new GoldDiscount());
29} elseif ($customer->isPlatinum()) {
30 $cart->setDiscountStrategy(new PlatinumDiscount());
31}
32
33// Calculate total with discount
34$total = $cart->calculateTotal();
به این صورت خیلی راحت میشه استراتژی های جدید رو بدون تغییر کد Context اضافه کرد.