我有一个允许客户购买汽车的端点。
PurchaseReq
结构看起来像这样:
type PurchaseReq struct {
Customer string
SalesRep string
Location string
Brand string
Model string
AddOns map[string]bool
}
PurchaseOrder
结构看起来像这样
type PurchaseOrder struct {
Customer string
SalesRep string
Price int
Brand string
Model string
AddOns map[string]bool
AfterSales map[string]string
}
Buy
函数看起来像这样。我目前使用 switch 语句来处理所有不同的品牌。价格和售后代表取决于品牌、地点等
func Buy(purchaseReq *PurchaseReq) error {
var price int
switch purchaseReq.Brand {
case "Ford":
price = getPriceFromFord(purchaseReq.Brand, purchaseReq.Model, purchaseReq.AddOns)
case "Ferrari":
price = getPriceFromFerrari(purchaseReq.Brand, purchaseReq.Model, purchaseReq.AddOns)
// ...
default:
return fmt.Errorf("invalid brand %s", purchaseReq.Brand)
}
afterSalesReps := getRepsFromStore(purchaseReq.Location, purchaseReq.Brand, purchaseReq.Model)
return order.New(&PurchaseOrder{
Customer : purchaseReq.Customer,
SalesRep : purchaseReq.SalesRep,
Brand : purchaseReq.Brand,
Model : purchaseReq.Model,
AddOns : purchaseReq.AddOns,
Price : price,
AfterSalesReps: afterSalesReps,
})
}
实际的结构比这复杂一点,每个汽车品牌都有更多的自定义逻辑。我还有大约 15 个汽车品牌在售。
通常我会使用多态性,所以我会
type Car interface{
Price() int
}
type Ford struct {}
func (f *Ford) Price() int { ... }
type Ferrari struct {}
func (f *Ferrari) Price() int { ... }
我的问题是,通过这个实现,我仍然会在
car.New
而不是 Buy
中有一个非常长的开关块。
func Buy(purchaseReq *PurchaseReq) error {
c := car.New(purchaseReq)
afterSalesReps := getRepsFromStore(purchaseReq.Location, purchaseReq.Brand, purchaseReq.Model)
po := &PurchaseOrder{
Customer : purchaseReq.Customer,
SalesRep : purchaseReq.SalesRep,
Brand : purchaseReq.Brand,
Model : purchaseReq.Model,
AddOns : purchaseReq.AddOns,
Price : c.Price(),
AfterSalesReps: afterSalesReps,
}
return order.New(po)
}
func New(purchaseReq *purchaseReq) *Car {
switch purchaseReq.Brand {
case "Ford":
return &Ford{ ... }
case "Ferrari":
return &Ferrari{ ... }
// ...
}
}
我实际上也没有对通用
Car
做任何事情,所以我实际上不需要这个接口。
我不确定实现这种设计的最佳方法是什么。有什么建议吗?
听起来在您的项目背景下可能会遇到多个问题需要解决,所以我将专注于我所看到的,而不是将您引向完全不同的方向。
首先,简化问题,去除不必要的上下文;
我需要将
的值链接到构造函数。string
我认为实现这种设计的最简洁方法是使用地图,您可以在其中使用品牌“key”,并返回构造它的函数;
brands = map[string]func() Car{
"Ford": func() Car { return NewFord() },
"Ferrari": func() Car { return NewFerrari() },
}
那么你可以简单地这样称呼它;
someFord := brands["Ford"]()
someFord.GetPrice()
或者
fmt.Println(brands["Ford"]().GetPrice())
显然我没有考虑到您的所有上下文和参数,但我希望这能在某种程度上帮助您实现它。
我在测试时写了一些代码here。