我们继续学习行为模式中的状态模式。
某个对象可能有很多的状态,且伴随着不同的状态有着不同的行为模式。而状态模式正是用来处理多种状态和行为的关系的。
在电商系统的中,订单有着非常多的状态,而不同的状态下订单的行为也是不同的。我们为了简化例子,假设订单一共有以下四种状态:
下单
已支付
出库
已取消
而围绕着订单的状态有以下行为:
加购
支付
取消
我们还是从反例开始理解状态模式。围绕着这些装的行为,在我们的代码中可能有着非常多的 if 或者switch语句来处理:
type Order struct {
orderID string
state int
}
const (
OrderAdded = 1
OrderPaied = 2
OrderSended = 3
OrderRefund = 4
)
func addOrder() Order {
return Order{
orderID: "xxx",
state: OrderAdded,
}
}
// 取消订单
func (o *Order) refund() {
switch o.state {
case OrderAdded:
// todo ...
case OrderPaied:
// todo ...
case OrderSended:
// todo ...
case OrderRefund:
// todo ...
}
}
这里只列举出来了refund 接口,需要根据订单的不同状态来做不同的处理。这里我们的示例只是有4个状态,而现实的电商系统状态要多得多,这样这个 switch 会特别的大(状态多了,行为也就更多,每个行为中的switch都特别大),影响代码阅读。
我们来看看状态模式如何处理以上逻辑呢?
type OrderAddedState struct {
order Order
}
type OrderPaiedState struct {
order Order
}
type OrderSendedState struct {
order Order
}
type OrderRefundState struct {
order Order
}
func (o *OrderAddedState) Refund() error {
//更新定单状态为已关闭
return nil
}
func (o *OrderPaiedState) Refund() error {
// 发起退款
//更新定单状态为已关闭
return nil
}
func (o *OrderSendedState) Refund() error {
// 取消发货
// 发起退款
// 更新定单状态为已关闭
return nil
}
func (o *OrderRefundState) Refund() error {
return errors.New("order has been refunded")
}
我们可以看到在不同的状态下的取消订单,封装到独立的类中去了,同时不同状态下的其他行为也可保证一样的处理,这样在不同状态下的行为需要处理哪些逻辑将一目了然,而无序去读很长且复杂的 siwtch 代码。
接下来我们了解行为模式中的策略模式。
策略模式可以使对象的行为可以灵活的在多个算法之间切换。我们还是用电商这个实际案例来了解策略模式。假设商品的活动有直接折扣、加价购、套装等销售策略,如何在下单时计算这个商品的价格呢?
我们还是从反例来说明以上逻辑,通常我们还是会用多个 if else 来根据不同的销售模式来计算商品的价格。且活动的优先级就已经通过if的层级进行划分,而如果不同的商品活动优先级不同,又需要额外的if来进行判断。从而使整个计算逻辑在多个且多层if else中处理,后期的维护越来越难。
我们看下策略模式是如何实现这个电商的实际场景的,首先我们定义计算逻辑的接口:
type orderAlgo struct {
}
type evictionAlgo interface {
evict(orderAlgo)
}
直接折扣、加价购、套装等具体的算法类将实现这个接口:
type cutoff struct {
}
type addPrice struct {
}
type suit struct {
}
func (cutoff) calc(calcuInfo) {
fmt.Println("普通折扣计算")
}
func (addPrice) calc(calcuInfo) {
fmt.Println("加价购折扣计算")
}
func (suit) calc(calcuInfo) {
fmt.Println("套装折扣计算")
}
这样每一个算法的逻辑在自己的成员函数中处理,而无需关注其他的计算逻辑,且不会像反例中一样所有的计算逻辑柔和在一起。同时多个活动的优先级可由计算算法的调用先后次序决定。
但是这里无法处理经过某个优惠后无法进行下一个优惠的实际场景,例如普通折扣后的商品无法再参与到套装活动中,那么需要调用方进行一系列的逻辑处理,例如给每种活动加上检查函数,先检查再调用。
近期回顾: