9.1.2.1 - توضیح الگوی Factory Method #
الگوی Factory یک الگوی طراحی خلاقانه است که یک رابط برای ایجاد اشیا در یک سوپرکلاس فراهم میکند، اما اجازه میدهد زیرکلاسها نوع شیءی که ساخته میشود را تغییر دهند. این الگو با جدا کردن منطق ساخت اشیا از کلاینتهایی که از آن اشیا استفاده میکنند، وابستگی مستقیم بین کلاینت و کلاسهای بتن را از بین میبرد. الگوی Factory زمانی مفید است که یک کلاس نتواند پیشبینی کند چه نوع کلاسی باید ایجاد کند، یا زمانی که یک کلاس بخواهد مسئولیت ایجاد اشیا را به زیرکلاسها محول کند. این الگو با کپسولهسازی منطق ایجاد، نگهداری کد را آسانتر کرده و اصل “Open/Closed” را رعایت میکند.
9.1.2.2 - مثال عملی #
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())
}
}
در این پیادهسازی الگوی Factory، رابط Vehicle به عنوان محصول پایه تعریف شده که متدهای Drive و GetType را شامل میشود. سه کلاس بتن Car، Motorcycle و Truck این رابط را پیادهسازی کرده و هر کدام رفتار خاص خود را ارائه میدهند. کلاس VehicleFactory به عنوان فکتوری عمل میکند که با متد CreateVehicle بر اساس نوع وسیله نقلیه درخواستی، نمونه مناسب را ایجاد و بازمیگرداند. همچنین یک تابع فکتوری مستقل به نام CreateVehicle نیز پیادهسازی شده که همان عملکرد را بدون نیاز به ایجاد نمونه از فکتوری ارائه میدهد. در تابع main، استفاده از هر دو روش فکتوری (هم از طریق ساختار و هم تابع) نمایش داده شده و نحوه ایجاد انواع مختلف وسایل نقلیه بدون وابستگی مستقیم به کلاسهای بتن نشان داده شده است. همچنین مدیریت خطا برای نوعهای ناشناخته و نمایش چندشکلی (polymorphism) با استفاده از آرایهای از رابط Vehicle نیز پیادهسازی شده است.