设计模式——策略模式

174 阅读4分钟

模版模式

类型

行为型设计模式

核心思想

策略模式的核心思想是将算法实现和调用者分离,将算法分别独立封装起来,让它们之间可以相互替换。使算法的变化,不会影响到使用算法的客户端

概述

策略模式通过将算法的实现与调用者分离,使得算法可以独立地变化,从而提高了代码的灵活性、可扩展性和可维护性。在策略模式中,通常会定义一个上下文类,它包含一个指向策略的引用,并且会将具体的算法委托给策略对象来实现。策略模式通过将算法的实现与调用者分离,使得算法可以灵活地应对不同的需求,同时也使得代码更加易于维护和扩展。

场景

  1. 针对同一类型的问题,有多种算法可以选择。
  2. 需要动态地在多种算法中选择一种。
  3. 算法的实现不影响客户端的代码。
  4. 需要避免使用大量的 if-else 或 switch-case 语句。

结构图

image.png

主要角色

  1. 上下文(context):封装了策略对象,调用执行具体的策略算法
  2. 抽象策略(Strategy):定义了一个公共的接口。
  3. 具体策略(Concrete Strategy):实现了抽象策略接口,封装了具体的算法。

优缺点

优点

  1. 可以动态地改变对象的行为,使得算法可以灵活地替换、新增或删除。
  2. 避免了大量的 if-else 或 switch-case 语句,使代码更加简洁、易于维护和扩展。
  3. 将算法封装起来,使得算法的实现细节被隐藏,客户端只需要关心算法的输入和输出即可。

缺点

  1. 增加了类的数量,使得代码变得更加复杂。
  2. 当策略对象过多时,客户端可能需要了解每个策略对象的区别,这会增加客户端的复杂度。
  3. 策略模式需要客户端自行选择合适的策略对象,可能会增加客户端的负担。

demo(golang)

商场针对不同级别用户的打折策略

抽象策略

// 策略接口
type DiscountStrategy interface {
    CalculateDiscount(price float64) float64
}

具体策略

// 普通用户折扣策略
type RegularDiscount struct{}

func (r *RegularDiscount) CalculateDiscount(price float64) float64 {
    return price * 0.95
}

// 会员折扣策略
type MemberDiscount struct{}

func (m *MemberDiscount) CalculateDiscount(price float64) float64 {
    return price * 0.9
}

// VIP折扣策略
type VipDiscount struct{}

func (v *VipDiscount) CalculateDiscount(price float64) float64 {
    return price * 0.8
}

上下文

// 上下文对象
type SalesContext struct {
    //包含抽象对象的接口成员,用于接收具体策略struct,然后调用
    discountStrategy DiscountStrategy
}

func NewSalesContext(discountStrategy DiscountStrategy) *SalesContext {
    return &SalesContext{discountStrategy}
}

func (s *SalesContext) SetDiscountStrategy(discountStrategy DiscountStrategy) {
    s.discountStrategy = discountStrategy
}
//具体调用
func (s *SalesContext) GetDiscountPrice(price float64) float64 {
    return s.discountStrategy.CalculateDiscount(price)
}

客户端对象

func main() {
    price := 100.0

    regularDiscount := &RegularDiscount{}
    memberDiscount := &MemberDiscount{}
    vipDiscount := &VipDiscount{}

    salesContext := NewSalesContext(regularDiscount)
    fmt.Printf("普通用户折扣后价格:%f\n", salesContext.GetDiscountPrice(price))

    salesContext.SetDiscountStrategy(memberDiscount)
    fmt.Printf("会员折扣后价格:%f\n", salesContext.GetDiscountPrice(price))

    salesContext.SetDiscountStrategy(vipDiscount)
    fmt.Printf("VIP折扣后价格:%f\n", salesContext.GetDiscountPrice(price))
}

这样子的实现其实是将选择具体使用哪个类型的用户放到了客户端进行实现,这里可以参考简单工厂的实现,将选择的逻辑封装到上下文,即将实例化具体策略的过程由客户端转移到上下文中。

上下文

// 上下文对象
type SalesContext struct {
    //包含抽象对象的接口成员,用于接收具体策略struct,然后调用
    discountStrategy DiscountStrategy
}

func NewSalesContext(type string) *SalesContext {
    switch type{
    case "普通用户":
        regularDiscount := &RegularDiscount{}
        return  &SalesContext{regularDiscount}
    case "会员":
        memberDiscount := &MemberDiscount{}
        return  &SalesContext{memberDiscount}
    case "VIP":
        vipDiscount := &VipDiscount{}
        return  &SalesContext{vipDiscount} 
    }
}

//具体调用
func (s *SalesContext) GetDiscountPrice(price float64) float64 {
    return s.discountStrategy.CalculateDiscount(price)
}

客户端对象

func main() {
    price := 100.0

    salesContext := NewSalesContext("普通用户")
    fmt.Printf("普通用户折扣后价格:%f\n", salesContext.GetDiscountPrice(price))
    salesContext := NewSalesContext("会员")
    fmt.Printf(会员折扣后价格:%f\n", salesContext.GetDiscountPrice(price))
    salesContext := NewSalesContext("VIP")
    fmt.Printf(VIP折扣后价格:%f\n", salesContext.GetDiscountPrice(price))
}

总结

策略模式是一种行为型设计模式,它通过将算法的实现与调用者分离,使得算法可以独立地变化,从而提高了代码的灵活性、可扩展性和可维护性

附加

大话设计模式里面提到: 1、面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。 2、讲不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的类中,就可以在使用这些行为的类中消除条件语句。 3、策略模式封装了变化,只要需要在不同时间应用不同的业务规则,都可以考虑使用策略模式处理这种变化