From a9bcc923c689621cdb64833f2826708aacb87df7 Mon Sep 17 00:00:00 2001 From: Amir Hasanpour Date: Fri, 24 Oct 2025 16:15:04 +0330 Subject: [PATCH 1/5] Modify 9.2.1 example format and make it executable --- .../structural patterns/9.2.1-adaptor.md | 129 ++++++++---------- 1 file changed, 56 insertions(+), 73 deletions(-) diff --git a/content/chapter 9/structural patterns/9.2.1-adaptor.md b/content/chapter 9/structural patterns/9.2.1-adaptor.md index 1cc5c289b..4c0429e6e 100644 --- a/content/chapter 9/structural patterns/9.2.1-adaptor.md +++ b/content/chapter 9/structural patterns/9.2.1-adaptor.md @@ -13,87 +13,70 @@ weight: 174001 در زیر مثالی از پیاده سازی این الگو انجام گرفته است. در این مثال دو دستگاه پرینتر متفاوت وظیفه چاپ بر روی کاغذ با یک ابعاد خاص و مشخص (A4) را دارند در صورتی که هیچکدام بصورت اختصاصی این ابعاد را پشتیبانی نمیکنند بنابراین ما با پیاده سازی الگوی adapter این امکان را ایجاد میکنیم که این تفاوت ابعاد در داخل هر موجودیت مدیریت شود و فقط متد چاپ در ابعاد A4 فراخوانی شود. -```go -package main - - -type IPrint interface{ -PrintA4() -} - -type Printer struct {} - -func (p Printer) Print(printer IPrint) { -printer.PrintA4() -} - -// HP Printer -type HpPrinter struct {} - -func (h HpPrinter) PrintWithHP() { -println("print with HP printer") -} - -type HpAdapter struct { -printer *HpPrinter -} - -func (ha HpAdapter) PrintA4() { -println("adapting to A4 size for HP printer") -//some adapting functions -ha.printer.PrintWithHP() -} - - -// Canon Printer -type CanonPrinter struct {} - -func (c CanonPrinter) PrintWithCanon() { -println("print with Canon printer") -} - -type CanonAdapter struct{ -printer *CanonPrinter -} - -func (ca CanonAdapter) PrintA4() { -println("adapting to A4 size for Canon printer") -//some adapting functions -ca.printer.PrintWithCanon() -} - - -func main() { - - -hpPrinter := &HpPrinter{} -hpAdapter := HpAdapter{ -printer: hpPrinter, -} - -canonPrinter := &CanonPrinter{} -canonAdapter := CanonAdapter{ -printer: canonPrinter, -} - -printer := Printer{} -printer.Print(hpAdapter) -printer.Print(canonAdapter) +{{< play >}} +package main + +type IPrint interface { + PrintA4() +} + +type Printer struct{} + +func (p Printer) Print(printer IPrint) { + printer.PrintA4() +} + +// HP Printer +type HpPrinter struct{} + +func (h HpPrinter) PrintWithHP() { + println("print with HP printer") +} + +type HpAdapter struct { + printer *HpPrinter +} + +func (ha HpAdapter) PrintA4() { + println("adapting to A4 size for HP printer") + //some adapting functions + ha.printer.PrintWithHP() } -``` +// Canon Printer +type CanonPrinter struct{} + +func (c CanonPrinter) PrintWithCanon() { + println("print with Canon printer") +} -```shell +type CanonAdapter struct { + printer *CanonPrinter +} -adapting to A4 size for HP printer -print with HP printer -adapting to A4 size for Canon printer -print with Canon printer +func (ca CanonAdapter) PrintA4() { + println("adapting to A4 size for Canon printer") + //some adapting functions + ca.printer.PrintWithCanon() +} -``` +func main() { + hpPrinter := &HpPrinter{} + hpAdapter := HpAdapter{ + printer: hpPrinter, + } + canonPrinter := &CanonPrinter{} + canonAdapter := CanonAdapter{ + printer: canonPrinter, + } + printer := Printer{} + printer.Print(hpAdapter) + printer.Print(canonAdapter) +} +{{< /play >}} ۱− در مثال بالا ما برای هر پرینتر یک struct به عنوان adapter ایجاد کرده ایم (خطوط 21 و 39) ۲− این struct ها متدی به نام PrintA4 را تعریف می‌کنند‌ که وظیفه تغییر داده ها به فرمت مشترک مورد نظر را دارند (خطوط 25 و 43) From af66c2326e096a2bf65d879d4ee563214355c129 Mon Sep 17 00:00:00 2001 From: Amir Hasanpour Date: Sun, 26 Oct 2025 00:59:13 +0330 Subject: [PATCH 2/5] Add Observer design pattern --- .../behavioral patterns/9.3.6-observer.md | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/content/chapter 9/behavioral patterns/9.3.6-observer.md b/content/chapter 9/behavioral patterns/9.3.6-observer.md index 900e72487..9955aa147 100644 --- a/content/chapter 9/behavioral patterns/9.3.6-observer.md +++ b/content/chapter 9/behavioral patterns/9.3.6-observer.md @@ -3,4 +3,104 @@ title: '9.3.6 الگو Observer' slug: go-observer-pattern weight: 176006 --- -الگو Observer... \ No newline at end of file + +## 9.3.6.1 - توضیح الگوی Observer + +الگوی Observer یک الگوی طراحی رفتاری است که رابطه‌ی یک-به-چند بین اشیا را تعریف می‌کند، به طوری که وقتی یک شیء (سوژه) حالت خود را تغییر می‌دهد، تمام اشیای وابسته به آن (ناظرها) به طور خودکار مطلع و به روز می‌شوند. این الگو برای ایجاد سیستم‌های event-driven ایده‌آل است و وابستگی قوی بین سوژه و ناظرها را از بین می‌برد، زیرا سوژه نیازی ندارد بداند ناظرهایش چه اشیایی هستند و فقط با آن‌ها از طریق یک رابط مشترک تعامل می‌کند. این الگو در موقعیت‌هایی مانند سیستم‌های اطلاع‌رسانی، رابط‌های کاربری reactive و رویدادهای real-time کاربرد گسترده‌ای دارد. + +## 9.3.6.2 - مثال عملی + +{{< play >}} +package main + +import "fmt" + +// Observer interface +type Observer interface { + Update(temperature float64) +} + +// Subject interface +type Subject interface { + RegisterObserver(o Observer) + RemoveObserver(o Observer) + NotifyObservers() +} + +// Concrete Subject - WeatherStation +type WeatherStation struct { + temperature float64 + observers []Observer +} + +func (w *WeatherStation) RegisterObserver(o Observer) { + w.observers = append(w.observers, o) +} + +func (w *WeatherStation) RemoveObserver(o Observer) { + for i, observer := range w.observers { + if observer == o { + w.observers = append(w.observers[:i], w.observers[i+1:]...) + break + } + } +} + +func (w *WeatherStation) NotifyObservers() { + for _, observer := range w.observers { + observer.Update(w.temperature) + } +} + +func (w *WeatherStation) SetTemperature(temp float64) { + w.temperature = temp + w.NotifyObservers() // Notify all observers when temperature changes +} + +// Concrete Observer - PhoneDisplay +type PhoneDisplay struct { + id string +} + +func (p *PhoneDisplay) Update(temperature float64) { + fmt.Printf("Phone Display %s: Temperature is %.1f°C\n", p.id, temperature) +} + +// Concrete Observer - TVDisplay +type TVDisplay struct { + channel string +} + +func (t *TVDisplay) Update(temperature float64) { + fmt.Printf("TV Display on %s: Current temperature: %.1f°C\n", t.channel, temperature) +} + +func main() { + // Create the subject (weather station) + weatherStation := &WeatherStation{} + + // Create observers + phone1 := &PhoneDisplay{id: "1"} + phone2 := &PhoneDisplay{id: "2"} + tv := &TVDisplay{channel: "Weather Channel"} + + // Register observers + weatherStation.RegisterObserver(phone1) + weatherStation.RegisterObserver(phone2) + weatherStation.RegisterObserver(tv) + + // Change temperature - all observers get notified + fmt.Println("Setting temperature to 25.5°C:") + weatherStation.SetTemperature(25.5) + + fmt.Println("\nSetting temperature to 30.2°C:") + weatherStation.SetTemperature(30.2) + + // Remove one observer + fmt.Println("\nRemoving Phone Display 2 and setting temperature to 28.0°C:") + weatherStation.RemoveObserver(phone2) + weatherStation.SetTemperature(28.0) +} +{{< /play >}} + +در این پیاده‌سازی الگوی Observer، دو رابط اصلی Observer و Subject تعریف شده‌اند. رابط Observer تنها متد Update را دارد که برای دریافت به‌روزرسانی‌ها از سوژه استفاده می‌شود. رابط Subject نیز متدهای RegisterObserver، RemoveObserver و NotifyObservers را برای مدیریت ناظرها تعریف می‌کند. کلاس WeatherStation به عنوان سوژهٔ بتن، این رابط را پیاده‌سازی کرده و دارای یک لیست از ناظرها است. وقتی دمای ایستگاه هواشناسی با متد SetTemperature تغییر می‌کند، متد NotifyObservers فراخوانی شده و تمام ناظرهای ثبت‌شده را با مقدار جدید دما به روز می‌کند. دو ناظر بتن PhoneDisplay و TVDisplay نیز رابط Observer را پیاده‌سازی کرده و هر کدام رفتار خاص خود را در متد Update تعریف می‌کنند. در تابع main، با ایجاد یک ایستگاه هواشناسی و چندین ناظر، نحوهٔ ثبت، حذف و اطلاع‌رسانی به ناظرها نمایش داده شده است. \ No newline at end of file From cfc416a33593d8a621bbcf12eaca89418e5075a2 Mon Sep 17 00:00:00 2001 From: Amir Hasanpour Date: Sun, 26 Oct 2025 01:37:25 +0330 Subject: [PATCH 3/5] Add Decorator design pattern --- .../structural patterns/9.2.4-decorator.md | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/content/chapter 9/structural patterns/9.2.4-decorator.md b/content/chapter 9/structural patterns/9.2.4-decorator.md index 6fb335c44..d29931d0f 100644 --- a/content/chapter 9/structural patterns/9.2.4-decorator.md +++ b/content/chapter 9/structural patterns/9.2.4-decorator.md @@ -4,4 +4,136 @@ slug: go-decorator-pattern weight: 174004 --- -الگو Decorator... \ No newline at end of file +## 9.2.4.1 - توضیح الگوی Decorator + +الگوی Decorator یک الگوی طراحی ساختاری است که به شما امکان می‌دهد رفتار یا مسئولیت‌های جدیدی را به یک شیء به صورت دینامیک و بدون تغییر ساختار اصلی آن اضافه کنید. این الگوی با پیاده‌سازی یک wrapper در اطراف شیء اصلی کار می‌کند و از ترکیب به جای ارث‌بری برای گسترش قابلیت‌ها استفاده می‌نماید. Decoratorها این امکان را فراهم می‌کنند که عملکرد یک شیء را به صورت پیمانه‌ای و مرحله‌ای افزایش دهید، در حالی که رابط یکسانی با شیء اصلی حفظ می‌شود. این الگو زمانی مفید است که نیاز به افزودن قابلیت‌های مختلف به شیء دارید اما نمی‌خواهید با ایجاد زیرکلاس‌های متعدد سلسله مراتب کلاس‌ها را پیچیده کنید. + +## 9.2.4.2 - مثال عملی + +{{< play >}} +package main + +import "fmt" + +// Component Interface +type Coffee interface { + Cost() float64 + Description() string +} + +// Concrete Component - Basic Coffee +type BasicCoffee struct{} + +func (b *BasicCoffee) Cost() float64 { + return 2.00 +} + +func (b *BasicCoffee) Description() string { + return "Basic Coffee" +} + +// Decorator Base +type CoffeeDecorator struct { + coffee Coffee +} + +func (c *CoffeeDecorator) Cost() float64 { + return c.coffee.Cost() +} + +func (c *CoffeeDecorator) Description() string { + return c.coffee.Description() +} + +// Concrete Decorator - Milk +type MilkDecorator struct { + CoffeeDecorator +} + +func NewMilkDecorator(coffee Coffee) *MilkDecorator { + return &MilkDecorator{CoffeeDecorator{coffee: coffee}} +} + +func (m *MilkDecorator) Cost() float64 { + return m.coffee.Cost() + 0.50 +} + +func (m *MilkDecorator) Description() string { + return m.coffee.Description() + ", Milk" +} + +// Concrete Decorator - Sugar +type SugarDecorator struct { + CoffeeDecorator +} + +func NewSugarDecorator(coffee Coffee) *SugarDecorator { + return &SugarDecorator{CoffeeDecorator{coffee: coffee}} +} + +func (s *SugarDecorator) Cost() float64 { + return s.coffee.Cost() + 0.25 +} + +func (s *SugarDecorator) Description() string { + return s.coffee.Description() + ", Sugar" +} + +// Concrete Decorator - Caramel Syrup +type CaramelDecorator struct { + CoffeeDecorator +} + +func NewCaramelDecorator(coffee Coffee) *CaramelDecorator { + return &CaramelDecorator{CoffeeDecorator{coffee: coffee}} +} + +func (c *CaramelDecorator) Cost() float64 { + return c.coffee.Cost() + 1.00 +} + +func (c *CaramelDecorator) Description() string { + return c.coffee.Description() + ", Caramel Syrup" +} + +func main() { + // Start with basic coffee + coffee := &BasicCoffee{} + fmt.Printf("%s: $%.2f\n", coffee.Description(), coffee.Cost()) + + // Add milk + coffeeWithMilk := NewMilkDecorator(coffee) + fmt.Printf("%s: $%.2f\n", coffeeWithMilk.Description(), coffeeWithMilk.Cost()) + + // Add sugar to coffee with milk + coffeeWithMilkAndSugar := NewSugarDecorator(coffeeWithMilk) + fmt.Printf("%s: $%.2f\n", coffeeWithMilkAndSugar.Description(), coffeeWithMilkAndSugar.Cost()) + + // Add caramel to everything + fancyCoffee := NewCaramelDecorator(coffeeWithMilkAndSugar) + fmt.Printf("%s: $%.2f\n", fancyCoffee.Description(), fancyCoffee.Cost()) + + // Let's create some different combinations + fmt.Println("\nDifferent coffee orders:") + + // Just caramel coffee + caramelCoffee := NewCaramelDecorator(&BasicCoffee{}) + fmt.Printf("%s: $%.2f\n", caramelCoffee.Description(), caramelCoffee.Cost()) + + // Coffee with sugar only + sweetCoffee := NewSugarDecorator(&BasicCoffee{}) + fmt.Printf("%s: $%.2f\n", sweetCoffee.Description(), sweetCoffee.Cost()) + + // Premium coffee with everything + premiumCoffee := NewCaramelDecorator( + NewMilkDecorator( + NewSugarDecorator( + &BasicCoffee{}, + ), + ), + ) + fmt.Printf("%s: $%.2f\n", premiumCoffee.Description(), premiumCoffee.Cost()) +} +{{< /play >}} + +در این پیاده‌سازی الگوی Decorator، رابط Coffee به عنوان کامپوننت پایه تعریف شده که متدهای Cost و Description را شامل می‌شود. کلاس BasicCoffee به عنوان Concrete Component، پیاده‌سازی ساده‌ای از این رابط ارائه می‌دهد. ساختار CoffeeDecorator به عنوان پایه برای تمام دکوراتورها عمل کرده و یک نمونه از Coffee را در خود نگهداری می‌کند. سه دکوراتور بتن MilkDecorator، SugarDecorator و CaramelDecorator از CoffeeDecorator embed کرده و هر کدام با اضافه کردن هزینه و توضیحات جدید، عملکرد قهوه پایه را گسترش می‌دهند. در تابع main، با ایجاد ترکیبات مختلفی از دکوراتورها روی قهوه پایه نشان داده شده که چگونه می‌توان به صورت پویا و مرحله‌ای قابلیت‌های جدیدی به شیء اضافه کرد، به طوری که از قهوه ساده شروع شده و به تدریج با اضافه کردن شیر، شکر و کارامل به یک قهوه مجلل تبدیل می‌شود، بدون اینکه ساختار اصلی کلاس BasicCoffee تغییر کند. \ No newline at end of file From 23eddeaf57fa9d45bf1ada5a788b76d1ae06de84 Mon Sep 17 00:00:00 2001 From: Amir Hasanpour Date: Sun, 26 Oct 2025 01:49:10 +0330 Subject: [PATCH 4/5] Add Factory Method design pattern --- .../9.1.2-factory-method.md | 120 +++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/content/chapter 9/creational patterns/9.1.2-factory-method.md b/content/chapter 9/creational patterns/9.1.2-factory-method.md index 30002fab6..4ed812970 100644 --- a/content/chapter 9/creational patterns/9.1.2-factory-method.md +++ b/content/chapter 9/creational patterns/9.1.2-factory-method.md @@ -4,4 +4,122 @@ slug: go-factory-method-pattern weight: 172002 --- -الگو Factory Method... \ No newline at end of file +## 9.1.2.1 - توضیح الگوی Factory Method + +الگوی Factory یک الگوی طراحی خلاقانه است که یک رابط برای ایجاد اشیا در یک سوپرکلاس فراهم می‌کند، اما اجازه می‌دهد زیرکلاس‌ها نوع شیءی که ساخته می‌شود را تغییر دهند. این الگو با جدا کردن منطق ساخت اشیا از کلاینت‌هایی که از آن اشیا استفاده می‌کنند، وابستگی مستقیم بین کلاینت و کلاس‌های بتن را از بین می‌برد. الگوی Factory زمانی مفید است که یک کلاس نتواند پیش‌بینی کند چه نوع کلاسی باید ایجاد کند، یا زمانی که یک کلاس بخواهد مسئولیت ایجاد اشیا را به زیرکلاس‌ها محول کند. این الگو با کپسوله‌سازی منطق ایجاد، نگهداری کد را آسان‌تر کرده و اصل "Open/Closed" را رعایت می‌کند. + +## 9.1.2.2 - مثال عملی + +{{< play >}} +package main + +import "fmt" + +// Product Interface +type Vehicle interface { + Drive() string + GetType() string +} + +// Concrete Products +type Car struct{} + +func (c *Car) Drive() string { + return "Driving a car on the road" +} + +func (c *Car) GetType() string { + return "Car" +} + +type Motorcycle struct{} + +func (m *Motorcycle) Drive() string { + return "Riding a motorcycle on the road" +} + +func (m *Motorcycle) GetType() string { + return "Motorcycle" +} + +type Truck struct{} + +func (t *Truck) Drive() string { + return "Driving a truck carrying heavy load" +} + +func (t *Truck) GetType() string { + return "Truck" +} + +// Factory +type VehicleFactory struct{} + +func (vf *VehicleFactory) CreateVehicle(vehicleType string) (Vehicle, error) { + switch vehicleType { + case "car": + return &Car{}, nil + case "motorcycle": + return &Motorcycle{}, nil + case "truck": + return &Truck{}, nil + default: + return nil, fmt.Errorf("unknown vehicle type: %s", vehicleType) + } +} + +// Alternatively, we can use factory functions instead of a struct +func CreateVehicle(vehicleType string) (Vehicle, error) { + switch vehicleType { + case "car": + return &Car{}, nil + case "motorcycle": + return &Motorcycle{}, nil + case "truck": + return &Truck{}, nil + default: + return nil, fmt.Errorf("unknown vehicle type: %s", vehicleType) + } +} + +func main() { + // Using the factory struct + factory := &VehicleFactory{} + + fmt.Println("Using VehicleFactory struct:") + car, _ := factory.CreateVehicle("car") + fmt.Printf("%s: %s\n", car.GetType(), car.Drive()) + + motorcycle, _ := factory.CreateVehicle("motorcycle") + fmt.Printf("%s: %s\n", motorcycle.GetType(), motorcycle.Drive()) + + truck, _ := factory.CreateVehicle("truck") + fmt.Printf("%s: %s\n", truck.GetType(), truck.Drive()) + + // Using the factory function + fmt.Println("\nUsing factory function:") + vehicle1, _ := CreateVehicle("car") + fmt.Printf("%s: %s\n", vehicle1.GetType(), vehicle1.Drive()) + + vehicle2, _ := CreateVehicle("motorcycle") + fmt.Printf("%s: %s\n", vehicle2.GetType(), vehicle2.Drive()) + + // Error handling + fmt.Println("\nTesting error handling:") + unknown, err := CreateVehicle("bicycle") + if err != nil { + fmt.Printf("Error: %s\n", err) + } else { + fmt.Printf("%s: %s\n", unknown.GetType(), unknown.Drive()) + } + + // Demonstrating polymorphism + fmt.Println("\nDemonstrating polymorphism:") + vehicles := []Vehicle{car, motorcycle, truck} + for _, vehicle := range vehicles { + fmt.Printf("- %s\n", vehicle.Drive()) + } +} +{{< /play >}} + +در این پیاده‌سازی الگوی Factory، رابط Vehicle به عنوان محصول پایه تعریف شده که متدهای Drive و GetType را شامل می‌شود. سه کلاس بتن Car، Motorcycle و Truck این رابط را پیاده‌سازی کرده و هر کدام رفتار خاص خود را ارائه می‌دهند. کلاس VehicleFactory به عنوان فکتوری عمل می‌کند که با متد CreateVehicle بر اساس نوع وسیله نقلیه درخواستی، نمونه مناسب را ایجاد و بازمی‌گرداند. همچنین یک تابع فکتوری مستقل به نام CreateVehicle نیز پیاده‌سازی شده که همان عملکرد را بدون نیاز به ایجاد نمونه از فکتوری ارائه می‌دهد. در تابع main، استفاده از هر دو روش فکتوری (هم از طریق ساختار و هم تابع) نمایش داده شده و نحوه ایجاد انواع مختلف وسایل نقلیه بدون وابستگی مستقیم به کلاس‌های بتن نشان داده شده است. همچنین مدیریت خطا برای نوع‌های ناشناخته و نمایش چندشکلی (polymorphism) با استفاده از آرایه‌ای از رابط Vehicle نیز پیاده‌سازی شده است. \ No newline at end of file From 0aa0cf610870cb01d4d53ff36f12d0f47b648846 Mon Sep 17 00:00:00 2001 From: Amir Hasanpour Date: Sun, 26 Oct 2025 02:01:09 +0330 Subject: [PATCH 5/5] Add Strategy design pattern --- .../behavioral patterns/9.3.8-strategy.md | 149 +++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/content/chapter 9/behavioral patterns/9.3.8-strategy.md b/content/chapter 9/behavioral patterns/9.3.8-strategy.md index 190de29a9..c27ac46b8 100644 --- a/content/chapter 9/behavioral patterns/9.3.8-strategy.md +++ b/content/chapter 9/behavioral patterns/9.3.8-strategy.md @@ -3,4 +3,151 @@ title: '9.3.8 الگو Strategy' slug: go-strategy-pattern weight: 176008 --- -الگو Strategy... \ No newline at end of file + +## 9.3.8.1 - توضیح الگوی Strategy + +الگوی Strategy یک الگوی طراحی رفتاری است که به شما امکان می‌دهد یک خانواده از الگوریتم‌ها را تعریف کرده، هر یک را در یک کلاس جداگانه کپسوله کنید و آن‌ها را قابل تعویض کنید. این الگو اجازه می‌دهد الگوریتم‌ها مستقل از کلاینت‌هایی که از آن‌ها استفاده می‌کنند، تغییر کنند. با استفاده از این الگو، می‌توانید رفتار یک کلاس را در زمان اجرا بدون تغییر ساختار آن کلاس تغییر دهید. Strategy وابستگی بین کلاینت و الگوریتم‌ها را از بین برده و اصل "Open/Closed" را رعایت می‌کند، به طوری که می‌توانید استراتژی‌های جدیدی اضافه کنید بدون اینکه کد موجود را تغییر دهید. + +## 9.3.8.2 - مثال عملی + +{{< play >}} +package main + +import "fmt" + +// Strategy Interface +type PaymentStrategy interface { + Pay(amount float64) string +} + +// Concrete Strategies +type CreditCardPayment struct { + cardNumber string + cvv string +} + +func NewCreditCardPayment(cardNumber, cvv string) *CreditCardPayment { + return &CreditCardPayment{ + cardNumber: cardNumber, + cvv: cvv, + } +} + +func (c *CreditCardPayment) Pay(amount float64) string { + return fmt.Sprintf("Paid $%.2f using Credit Card ending with %s", amount, c.cardNumber[len(c.cardNumber)-4:]) +} + +type PayPalPayment struct { + email string +} + +func NewPayPalPayment(email string) *PayPalPayment { + return &PayPalPayment{email: email} +} + +func (p *PayPalPayment) Pay(amount float64) string { + return fmt.Sprintf("Paid $%.2f using PayPal account %s", amount, p.email) +} + +type CryptoPayment struct { + walletAddress string +} + +func NewCryptoPayment(walletAddress string) *CryptoPayment { + return &CryptoPayment{walletAddress: walletAddress} +} + +func (c *CryptoPayment) Pay(amount float64) string { + return fmt.Sprintf("Paid $%.2f using Crypto wallet %s", amount, c.walletAddress[:8]+"...") +} + +type BankTransferPayment struct { + accountNumber string +} + +func NewBankTransferPayment(accountNumber string) *BankTransferPayment { + return &BankTransferPayment{accountNumber: accountNumber} +} + +func (b *BankTransferPayment) Pay(amount float64) string { + return fmt.Sprintf("Paid $%.2f using Bank Transfer to account %s", amount, b.accountNumber) +} + +// Context +type PaymentProcessor struct { + strategy PaymentStrategy +} + +func NewPaymentProcessor() *PaymentProcessor { + return &PaymentProcessor{} +} + +func (p *PaymentProcessor) SetStrategy(strategy PaymentStrategy) { + p.strategy = strategy +} + +func (p *PaymentProcessor) ProcessPayment(amount float64) string { + if p.strategy == nil { + return "Error: No payment strategy set" + } + return p.strategy.Pay(amount) +} + +// Alternative simpler approach using function +func ProcessPayment(strategy PaymentStrategy, amount float64) string { + return strategy.Pay(amount) +} + +func main() { + // Using the Context approach + processor := NewPaymentProcessor() + + fmt.Println("=== Using PaymentProcessor Context ===") + + // Credit Card payment + creditCard := NewCreditCardPayment("4111111111111111", "123") + processor.SetStrategy(creditCard) + fmt.Println(processor.ProcessPayment(99.99)) + + // PayPal payment + paypal := NewPayPalPayment("user@example.com") + processor.SetStrategy(paypal) + fmt.Println(processor.ProcessPayment(49.50)) + + // Crypto payment + crypto := NewCryptoPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa") + processor.SetStrategy(crypto) + fmt.Println(processor.ProcessPayment(150.00)) + + fmt.Println("\n=== Using Direct Function Approach ===") + + // Using the function approach + bankTransfer := NewBankTransferPayment("123456789") + fmt.Println(ProcessPayment(bankTransfer, 75.25)) + fmt.Println(ProcessPayment(creditCard, 29.99)) + + fmt.Println("\n=== Dynamic Strategy Switching ===") + + // Demonstrating runtime strategy changes + shoppingCart := []float64{25.00, 60.00, 15.50} + + // Process with different strategies + strategies := []PaymentStrategy{ + creditCard, + paypal, + crypto, + } + + for i, amount := range shoppingCart { + strategy := strategies[i%len(strategies)] + processor.SetStrategy(strategy) + fmt.Printf("Item $%.2f: %s\n", amount, processor.ProcessPayment(amount)) + } + + fmt.Println("\n=== Error Handling ===") + emptyProcessor := NewPaymentProcessor() + fmt.Println(emptyProcessor.ProcessPayment(100.00)) +} +{{< /play >}} + +در این پیاده‌سازی الگوی Strategy، رابط PaymentStrategy به عنوان استراتژی پایه تعریف شده که متد Pay را شامل می‌شود. چهار استراتژی بتن CreditCardPayment، PayPalPayment، CryptoPayment و BankTransferPayment این رابط را پیاده‌سازی کرده و هر کدام منطق پرداخت خاص خود را ارائه می‌دهند. کلاس PaymentProcessor به عنوان Context عمل می‌کند که یک استراتژی پرداخت را نگهداری کرده و با متد SetStrategy امکان تغییر استراتژی در زمان اجرا را فراهم می‌کند. متد ProcessPayment در Context، کار را به استراتژی فعلی واگذار می‌کند. همچنین یک تابع ساده به نام ProcessPayment به عنوان رویکرد جایگزین پیاده‌سازی شده است. در تابع main، استفاده از هر دو رویکرد (Context و تابع مستقیم) نمایش داده شده و نحوه تغییر پویای استراتژی‌ها در زمان اجرا نشان داده شده است. همچنین سناریویی برای پرداخت‌های متعدد با استراتژی‌های مختلف و مدیریت خطا برای زمانی که استراتژی تنظیم نشده، پیاده‌سازی شده است. \ No newline at end of file