需求

作为电子商务网站拥有者,提供产品折扣来吸引和留住客户非常重要。在本博客文章中,我们将探讨如何使用Go语言中的策略模式实现商品的简单打折功能。

实现

策略模式需要先定义一个打折的抽象接口,每一种打折算法都要实现该接口,打折方式分别是正常收费、打折收费和返利收费:

package cash

// Cash 现金收费接口
type Cash interface {
    AccessCash(money float64) float64
}

正常收费:

package cash

// Normal 正常收费
type Normal struct{}

// AccessCash 正常收费,原价返回原价
func (n *Normal) AccessCash(money float64) float64 {
    return money
}

打折收费:

package cash

// Rebate 打折收费
type Rebate struct {
    MoneyRebate float64 // 折扣率
}

// AccessCash 打折收费
func (r *Rebate) AccessCash(money float64) float64 {
    return money * r.MoneyRebate
}

返利收费:

package cash

import "math"

// Return 返利收费
type Return struct {
    MoneyCondition float64 // 返利条件
    MoneyReturn    float64 // 返利值
}

// AccessCash 返利收费
func (r *Return) AccessCash(money float64) float64 {
    if money >= r.MoneyCondition {
        return money - math.Floor(money/r.MoneyCondition)*r.MoneyReturn
    }
    return money
}

一般策略模式需要跟另外一个类结合,这个类的功能就是根据不同的传参来返回不同的打折类或者最终结算金额。如果要返回类,那么就是使用了工厂模式,客户端要调用就需要知道打折工厂类和抽象接口。还有另外比较便捷的做法就是实现上下文的类,这个上下文内部维持一个类似工厂模式的功能,然后再定义一个公开获取结算金额的方法:

package strategy

import "oop/strategy/cash"

type CashContext struct {
    strategy cash.Cash
}

func NewCashContext(t string) *CashContext {
    context := &CashContext{}
    switch t {
    case "正常收费":
        context.strategy = &cash.Normal{}
    case "满300返100":
        context.strategy = &cash.Return{MoneyCondition: 300, MoneyReturn: 100}
    case "打8折":
        context.strategy = &cash.Rebate{MoneyRebate: 0.8}
    default:
        context.strategy = &cash.Normal{}
    }

    return context
}

func (c *CashContext) GetResult(money float64) float64 {
    return c.strategy.AccessCash(money)
}

为了方便测试,再定义一个产品结构体:

package strategy

type Product struct {
    Name  string
    Price float64
}

最后写一个单元测试,我这边新建一个cash_test.go

package strategy

import "testing"

func TestCast(t *testing.T) {
    var products []*Product

    // 声明一个苹果手机产品
    products = append(products, &Product{
        Name:  "苹果",
        Price: 200,
    })

    // 声明一个华为手机产品
    products = append(products, &Product{
        Name:  "华为",
        Price: 300,
    })

    // 计算总价
    var total float64
    for _, product := range products {
        total += product.Price
    }

    // 声明一个正常收费的上下文对象
    context := NewCashContext("正常收费")

    // 结算金额
    var settlement float64

    settlement = context.GetResult(total)
    t.Logf("正常收费,总价为:%.2f,结算金额为:%.2f\n", total, settlement)

    // 声明一个满300返100的上下文对象
    context = NewCashContext("满300返100")
    settlement = context.GetResult(total)
    t.Logf("满300返100,总价为:%.2f,结算金额为:%.2f\n", total, settlement)

    // 声明一个打8折的上下文对象
    context = NewCashContext("打8折")
    settlement = context.GetResult(total)
    t.Logf("打8折,总价为:%.2f,结算金额为:%.2f\n", total, settlement)

}

运行结果如下:

=== RUN   TestCast
    cash_test.go:33: 正常收费,总价为:500.00,结算金额为:500.00
    cash_test.go:38: 满300返100,总价为:500.00,结算金额为:400.00
    cash_test.go:43: 打8折,总价为:500.00,结算金额为:400.00
--- PASS: TestCast (0.00s)
PASS

总结

在本博客文章中,我们探讨了如何使用Go语言中的策略模式实现商品的简单打折功能。通过使用策略模式,我们定义了一组算法、封装了每个算法,并使它们可以互换,可以根据需要使用代码实现来扩展额外的策略或要求。