Skip to content

Commit 0aa0cf6

Browse files
committed
Add Strategy design pattern
1 parent 23eddea commit 0aa0cf6

File tree

1 file changed

+148
-1
lines changed

1 file changed

+148
-1
lines changed

content/chapter 9/behavioral patterns/9.3.8-strategy.md

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,151 @@ title: '9.3.8 الگو Strategy'
33
slug: go-strategy-pattern
44
weight: 176008
55
---
6-
الگو Strategy...
6+
7+
## 9.3.8.1 - توضیح الگوی Strategy
8+
9+
الگوی Strategy یک الگوی طراحی رفتاری است که به شما امکان می‌دهد یک خانواده از الگوریتم‌ها را تعریف کرده، هر یک را در یک کلاس جداگانه کپسوله کنید و آن‌ها را قابل تعویض کنید. این الگو اجازه می‌دهد الگوریتم‌ها مستقل از کلاینت‌هایی که از آن‌ها استفاده می‌کنند، تغییر کنند. با استفاده از این الگو، می‌توانید رفتار یک کلاس را در زمان اجرا بدون تغییر ساختار آن کلاس تغییر دهید. Strategy وابستگی بین کلاینت و الگوریتم‌ها را از بین برده و اصل "Open/Closed" را رعایت می‌کند، به طوری که می‌توانید استراتژی‌های جدیدی اضافه کنید بدون اینکه کد موجود را تغییر دهید.
10+
11+
## 9.3.8.2 - مثال عملی
12+
13+
{{< play >}}
14+
package main
15+
16+
import "fmt"
17+
18+
// Strategy Interface
19+
type PaymentStrategy interface {
20+
Pay(amount float64) string
21+
}
22+
23+
// Concrete Strategies
24+
type CreditCardPayment struct {
25+
cardNumber string
26+
cvv string
27+
}
28+
29+
func NewCreditCardPayment(cardNumber, cvv string) *CreditCardPayment {
30+
return &CreditCardPayment{
31+
cardNumber: cardNumber,
32+
cvv: cvv,
33+
}
34+
}
35+
36+
func (c *CreditCardPayment) Pay(amount float64) string {
37+
return fmt.Sprintf("Paid $%.2f using Credit Card ending with %s", amount, c.cardNumber[len(c.cardNumber)-4:])
38+
}
39+
40+
type PayPalPayment struct {
41+
email string
42+
}
43+
44+
func NewPayPalPayment(email string) *PayPalPayment {
45+
return &PayPalPayment{email: email}
46+
}
47+
48+
func (p *PayPalPayment) Pay(amount float64) string {
49+
return fmt.Sprintf("Paid $%.2f using PayPal account %s", amount, p.email)
50+
}
51+
52+
type CryptoPayment struct {
53+
walletAddress string
54+
}
55+
56+
func NewCryptoPayment(walletAddress string) *CryptoPayment {
57+
return &CryptoPayment{walletAddress: walletAddress}
58+
}
59+
60+
func (c *CryptoPayment) Pay(amount float64) string {
61+
return fmt.Sprintf("Paid $%.2f using Crypto wallet %s", amount, c.walletAddress[:8]+"...")
62+
}
63+
64+
type BankTransferPayment struct {
65+
accountNumber string
66+
}
67+
68+
func NewBankTransferPayment(accountNumber string) *BankTransferPayment {
69+
return &BankTransferPayment{accountNumber: accountNumber}
70+
}
71+
72+
func (b *BankTransferPayment) Pay(amount float64) string {
73+
return fmt.Sprintf("Paid $%.2f using Bank Transfer to account %s", amount, b.accountNumber)
74+
}
75+
76+
// Context
77+
type PaymentProcessor struct {
78+
strategy PaymentStrategy
79+
}
80+
81+
func NewPaymentProcessor() *PaymentProcessor {
82+
return &PaymentProcessor{}
83+
}
84+
85+
func (p *PaymentProcessor) SetStrategy(strategy PaymentStrategy) {
86+
p.strategy = strategy
87+
}
88+
89+
func (p *PaymentProcessor) ProcessPayment(amount float64) string {
90+
if p.strategy == nil {
91+
return "Error: No payment strategy set"
92+
}
93+
return p.strategy.Pay(amount)
94+
}
95+
96+
// Alternative simpler approach using function
97+
func ProcessPayment(strategy PaymentStrategy, amount float64) string {
98+
return strategy.Pay(amount)
99+
}
100+
101+
func main() {
102+
// Using the Context approach
103+
processor := NewPaymentProcessor()
104+
105+
fmt.Println("=== Using PaymentProcessor Context ===")
106+
107+
// Credit Card payment
108+
creditCard := NewCreditCardPayment("4111111111111111", "123")
109+
processor.SetStrategy(creditCard)
110+
fmt.Println(processor.ProcessPayment(99.99))
111+
112+
// PayPal payment
113+
paypal := NewPayPalPayment("[email protected]")
114+
processor.SetStrategy(paypal)
115+
fmt.Println(processor.ProcessPayment(49.50))
116+
117+
// Crypto payment
118+
crypto := NewCryptoPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")
119+
processor.SetStrategy(crypto)
120+
fmt.Println(processor.ProcessPayment(150.00))
121+
122+
fmt.Println("\n=== Using Direct Function Approach ===")
123+
124+
// Using the function approach
125+
bankTransfer := NewBankTransferPayment("123456789")
126+
fmt.Println(ProcessPayment(bankTransfer, 75.25))
127+
fmt.Println(ProcessPayment(creditCard, 29.99))
128+
129+
fmt.Println("\n=== Dynamic Strategy Switching ===")
130+
131+
// Demonstrating runtime strategy changes
132+
shoppingCart := []float64{25.00, 60.00, 15.50}
133+
134+
// Process with different strategies
135+
strategies := []PaymentStrategy{
136+
creditCard,
137+
paypal,
138+
crypto,
139+
}
140+
141+
for i, amount := range shoppingCart {
142+
strategy := strategies[i%len(strategies)]
143+
processor.SetStrategy(strategy)
144+
fmt.Printf("Item $%.2f: %s\n", amount, processor.ProcessPayment(amount))
145+
}
146+
147+
fmt.Println("\n=== Error Handling ===")
148+
emptyProcessor := NewPaymentProcessor()
149+
fmt.Println(emptyProcessor.ProcessPayment(100.00))
150+
}
151+
{{< /play >}}
152+
153+
در این پیاده‌سازی الگوی Strategy، رابط PaymentStrategy به عنوان استراتژی پایه تعریف شده که متد Pay را شامل می‌شود. چهار استراتژی بتن CreditCardPayment، PayPalPayment، CryptoPayment و BankTransferPayment این رابط را پیاده‌سازی کرده و هر کدام منطق پرداخت خاص خود را ارائه می‌دهند. کلاس PaymentProcessor به عنوان Context عمل می‌کند که یک استراتژی پرداخت را نگهداری کرده و با متد SetStrategy امکان تغییر استراتژی در زمان اجرا را فراهم می‌کند. متد ProcessPayment در Context، کار را به استراتژی فعلی واگذار می‌کند. همچنین یک تابع ساده به نام ProcessPayment به عنوان رویکرد جایگزین پیاده‌سازی شده است. در تابع main، استفاده از هر دو رویکرد (Context و تابع مستقیم) نمایش داده شده و نحوه تغییر پویای استراتژی‌ها در زمان اجرا نشان داده شده است. همچنین سناریویی برای پرداخت‌های متعدد با استراتژی‌های مختلف و مدیریت خطا برای زمانی که استراتژی تنظیم نشده، پیاده‌سازی شده است.

0 commit comments

Comments
 (0)