Skip to content

Commit eb1cad8

Browse files
authored
Merge pull request #400 from amirhasanpour/design-patterns
Design Patterns
2 parents 5cf78f9 + 0aa0cf6 commit eb1cad8

File tree

5 files changed

+557
-77
lines changed

5 files changed

+557
-77
lines changed

content/chapter 9/behavioral patterns/9.3.6-observer.md

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,104 @@ title: '9.3.6 الگو Observer'
33
slug: go-observer-pattern
44
weight: 176006
55
---
6-
الگو Observer...
6+
7+
## 9.3.6.1 - توضیح الگوی Observer
8+
9+
الگوی Observer یک الگوی طراحی رفتاری است که رابطه‌ی یک-به-چند بین اشیا را تعریف می‌کند، به طوری که وقتی یک شیء (سوژه) حالت خود را تغییر می‌دهد، تمام اشیای وابسته به آن (ناظرها) به طور خودکار مطلع و به روز می‌شوند. این الگو برای ایجاد سیستم‌های event-driven ایده‌آل است و وابستگی قوی بین سوژه و ناظرها را از بین می‌برد، زیرا سوژه نیازی ندارد بداند ناظرهایش چه اشیایی هستند و فقط با آن‌ها از طریق یک رابط مشترک تعامل می‌کند. این الگو در موقعیت‌هایی مانند سیستم‌های اطلاع‌رسانی، رابط‌های کاربری reactive و رویدادهای real-time کاربرد گسترده‌ای دارد.
10+
11+
## 9.3.6.2 - مثال عملی
12+
13+
{{< play >}}
14+
package main
15+
16+
import "fmt"
17+
18+
// Observer interface
19+
type Observer interface {
20+
Update(temperature float64)
21+
}
22+
23+
// Subject interface
24+
type Subject interface {
25+
RegisterObserver(o Observer)
26+
RemoveObserver(o Observer)
27+
NotifyObservers()
28+
}
29+
30+
// Concrete Subject - WeatherStation
31+
type WeatherStation struct {
32+
temperature float64
33+
observers []Observer
34+
}
35+
36+
func (w *WeatherStation) RegisterObserver(o Observer) {
37+
w.observers = append(w.observers, o)
38+
}
39+
40+
func (w *WeatherStation) RemoveObserver(o Observer) {
41+
for i, observer := range w.observers {
42+
if observer == o {
43+
w.observers = append(w.observers[:i], w.observers[i+1:]...)
44+
break
45+
}
46+
}
47+
}
48+
49+
func (w *WeatherStation) NotifyObservers() {
50+
for _, observer := range w.observers {
51+
observer.Update(w.temperature)
52+
}
53+
}
54+
55+
func (w *WeatherStation) SetTemperature(temp float64) {
56+
w.temperature = temp
57+
w.NotifyObservers() // Notify all observers when temperature changes
58+
}
59+
60+
// Concrete Observer - PhoneDisplay
61+
type PhoneDisplay struct {
62+
id string
63+
}
64+
65+
func (p *PhoneDisplay) Update(temperature float64) {
66+
fmt.Printf("Phone Display %s: Temperature is %.1f°C\n", p.id, temperature)
67+
}
68+
69+
// Concrete Observer - TVDisplay
70+
type TVDisplay struct {
71+
channel string
72+
}
73+
74+
func (t *TVDisplay) Update(temperature float64) {
75+
fmt.Printf("TV Display on %s: Current temperature: %.1f°C\n", t.channel, temperature)
76+
}
77+
78+
func main() {
79+
// Create the subject (weather station)
80+
weatherStation := &WeatherStation{}
81+
82+
// Create observers
83+
phone1 := &PhoneDisplay{id: "1"}
84+
phone2 := &PhoneDisplay{id: "2"}
85+
tv := &TVDisplay{channel: "Weather Channel"}
86+
87+
// Register observers
88+
weatherStation.RegisterObserver(phone1)
89+
weatherStation.RegisterObserver(phone2)
90+
weatherStation.RegisterObserver(tv)
91+
92+
// Change temperature - all observers get notified
93+
fmt.Println("Setting temperature to 25.5°C:")
94+
weatherStation.SetTemperature(25.5)
95+
96+
fmt.Println("\nSetting temperature to 30.2°C:")
97+
weatherStation.SetTemperature(30.2)
98+
99+
// Remove one observer
100+
fmt.Println("\nRemoving Phone Display 2 and setting temperature to 28.0°C:")
101+
weatherStation.RemoveObserver(phone2)
102+
weatherStation.SetTemperature(28.0)
103+
}
104+
{{< /play >}}
105+
106+
در این پیاده‌سازی الگوی Observer، دو رابط اصلی Observer و Subject تعریف شده‌اند. رابط Observer تنها متد Update را دارد که برای دریافت به‌روزرسانی‌ها از سوژه استفاده می‌شود. رابط Subject نیز متدهای RegisterObserver، RemoveObserver و NotifyObservers را برای مدیریت ناظرها تعریف می‌کند. کلاس WeatherStation به عنوان سوژهٔ بتن، این رابط را پیاده‌سازی کرده و دارای یک لیست از ناظرها است. وقتی دمای ایستگاه هواشناسی با متد SetTemperature تغییر می‌کند، متد NotifyObservers فراخوانی شده و تمام ناظرهای ثبت‌شده را با مقدار جدید دما به روز می‌کند. دو ناظر بتن PhoneDisplay و TVDisplay نیز رابط Observer را پیاده‌سازی کرده و هر کدام رفتار خاص خود را در متد Update تعریف می‌کنند. در تابع main، با ایجاد یک ایستگاه هواشناسی و چندین ناظر، نحوهٔ ثبت، حذف و اطلاع‌رسانی به ناظرها نمایش داده شده است.

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 و تابع مستقیم) نمایش داده شده و نحوه تغییر پویای استراتژی‌ها در زمان اجرا نشان داده شده است. همچنین سناریویی برای پرداخت‌های متعدد با استراتژی‌های مختلف و مدیریت خطا برای زمانی که استراتژی تنظیم نشده، پیاده‌سازی شده است.

content/chapter 9/creational patterns/9.1.2-factory-method.md

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,122 @@ slug: go-factory-method-pattern
44
weight: 172002
55
---
66

7-
الگو Factory Method...
7+
## 9.1.2.1 - توضیح الگوی Factory Method
8+
9+
الگوی Factory یک الگوی طراحی خلاقانه است که یک رابط برای ایجاد اشیا در یک سوپرکلاس فراهم می‌کند، اما اجازه می‌دهد زیرکلاس‌ها نوع شیءی که ساخته می‌شود را تغییر دهند. این الگو با جدا کردن منطق ساخت اشیا از کلاینت‌هایی که از آن اشیا استفاده می‌کنند، وابستگی مستقیم بین کلاینت و کلاس‌های بتن را از بین می‌برد. الگوی Factory زمانی مفید است که یک کلاس نتواند پیش‌بینی کند چه نوع کلاسی باید ایجاد کند، یا زمانی که یک کلاس بخواهد مسئولیت ایجاد اشیا را به زیرکلاس‌ها محول کند. این الگو با کپسوله‌سازی منطق ایجاد، نگهداری کد را آسان‌تر کرده و اصل "Open/Closed" را رعایت می‌کند.
10+
11+
## 9.1.2.2 - مثال عملی
12+
13+
{{< play >}}
14+
package main
15+
16+
import "fmt"
17+
18+
// Product Interface
19+
type Vehicle interface {
20+
Drive() string
21+
GetType() string
22+
}
23+
24+
// Concrete Products
25+
type Car struct{}
26+
27+
func (c *Car) Drive() string {
28+
return "Driving a car on the road"
29+
}
30+
31+
func (c *Car) GetType() string {
32+
return "Car"
33+
}
34+
35+
type Motorcycle struct{}
36+
37+
func (m *Motorcycle) Drive() string {
38+
return "Riding a motorcycle on the road"
39+
}
40+
41+
func (m *Motorcycle) GetType() string {
42+
return "Motorcycle"
43+
}
44+
45+
type Truck struct{}
46+
47+
func (t *Truck) Drive() string {
48+
return "Driving a truck carrying heavy load"
49+
}
50+
51+
func (t *Truck) GetType() string {
52+
return "Truck"
53+
}
54+
55+
// Factory
56+
type VehicleFactory struct{}
57+
58+
func (vf *VehicleFactory) CreateVehicle(vehicleType string) (Vehicle, error) {
59+
switch vehicleType {
60+
case "car":
61+
return &Car{}, nil
62+
case "motorcycle":
63+
return &Motorcycle{}, nil
64+
case "truck":
65+
return &Truck{}, nil
66+
default:
67+
return nil, fmt.Errorf("unknown vehicle type: %s", vehicleType)
68+
}
69+
}
70+
71+
// Alternatively, we can use factory functions instead of a struct
72+
func CreateVehicle(vehicleType string) (Vehicle, error) {
73+
switch vehicleType {
74+
case "car":
75+
return &Car{}, nil
76+
case "motorcycle":
77+
return &Motorcycle{}, nil
78+
case "truck":
79+
return &Truck{}, nil
80+
default:
81+
return nil, fmt.Errorf("unknown vehicle type: %s", vehicleType)
82+
}
83+
}
84+
85+
func main() {
86+
// Using the factory struct
87+
factory := &VehicleFactory{}
88+
89+
fmt.Println("Using VehicleFactory struct:")
90+
car, _ := factory.CreateVehicle("car")
91+
fmt.Printf("%s: %s\n", car.GetType(), car.Drive())
92+
93+
motorcycle, _ := factory.CreateVehicle("motorcycle")
94+
fmt.Printf("%s: %s\n", motorcycle.GetType(), motorcycle.Drive())
95+
96+
truck, _ := factory.CreateVehicle("truck")
97+
fmt.Printf("%s: %s\n", truck.GetType(), truck.Drive())
98+
99+
// Using the factory function
100+
fmt.Println("\nUsing factory function:")
101+
vehicle1, _ := CreateVehicle("car")
102+
fmt.Printf("%s: %s\n", vehicle1.GetType(), vehicle1.Drive())
103+
104+
vehicle2, _ := CreateVehicle("motorcycle")
105+
fmt.Printf("%s: %s\n", vehicle2.GetType(), vehicle2.Drive())
106+
107+
// Error handling
108+
fmt.Println("\nTesting error handling:")
109+
unknown, err := CreateVehicle("bicycle")
110+
if err != nil {
111+
fmt.Printf("Error: %s\n", err)
112+
} else {
113+
fmt.Printf("%s: %s\n", unknown.GetType(), unknown.Drive())
114+
}
115+
116+
// Demonstrating polymorphism
117+
fmt.Println("\nDemonstrating polymorphism:")
118+
vehicles := []Vehicle{car, motorcycle, truck}
119+
for _, vehicle := range vehicles {
120+
fmt.Printf("- %s\n", vehicle.Drive())
121+
}
122+
}
123+
{{< /play >}}
124+
125+
در این پیاده‌سازی الگوی Factory، رابط Vehicle به عنوان محصول پایه تعریف شده که متدهای Drive و GetType را شامل می‌شود. سه کلاس بتن Car، Motorcycle و Truck این رابط را پیاده‌سازی کرده و هر کدام رفتار خاص خود را ارائه می‌دهند. کلاس VehicleFactory به عنوان فکتوری عمل می‌کند که با متد CreateVehicle بر اساس نوع وسیله نقلیه درخواستی، نمونه مناسب را ایجاد و بازمی‌گرداند. همچنین یک تابع فکتوری مستقل به نام CreateVehicle نیز پیاده‌سازی شده که همان عملکرد را بدون نیاز به ایجاد نمونه از فکتوری ارائه می‌دهد. در تابع main، استفاده از هر دو روش فکتوری (هم از طریق ساختار و هم تابع) نمایش داده شده و نحوه ایجاد انواع مختلف وسایل نقلیه بدون وابستگی مستقیم به کلاس‌های بتن نشان داده شده است. همچنین مدیریت خطا برای نوع‌های ناشناخته و نمایش چندشکلی (polymorphism) با استفاده از آرایه‌ای از رابط Vehicle نیز پیاده‌سازی شده است.

0 commit comments

Comments
 (0)