golang设计模式——策略模式

策略模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T8FSW1Su-1660572483018)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220815114229835.png)]

策略模式核心在于利用多态性,这是目前主流的面向对象语言都支持的功能。

策略模式:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

UML

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eTd6UJnE-1660572483020)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220815214739407.png)]

分析

单看策略模式,主要利用多态性。但策略模式往往不单独使用,它会和工厂模式配合使用。此时策略模式便可解耦策略的定义、创建、使用。

策略模式加工厂模式,能够达到去除if-else和switch的效果,主要靠工厂模式加持,借助于“查表法”找到指定策略进行使用。

应用场景

策略模式的应用场景还是蛮广泛的,处理同一类问题如果有多种算法,就可以使用策略模式。如根据不同活动计算优惠价格、根据商品不同类型计算税率等。

最近有一个实际业务场景可以使用策略模式,计算跨境商品税费。

跨境商品的税费和两方面有关,一是商品是否含税,二是商品类型,不同类型对应税率不一样,如常规商品和酒类商品税率不一样、税的计算方式也不一样。

所以只要知道商品是否含税、商品类型就能找到对应的计算方案。如果我们使用if-else来写,会不优雅,因为商品类型比较多,而且计算逻辑也相对复杂,所以我们可以利用查表法进行优化。

税费计算接口被交易侧调用,交易调用的时候会传商品是否含税,商品类型由项目组自行维护。那我们来看一下具体实现。

代码实现

package main

import "fmt"

const (
   Common = "COMMON"
   Win    = "WIN"
)

/**
 * @Description: 根据hscode获取商品类型
 * @param hscode
 * @return string
 */
func getProductType(hscode string) string {
   if hscode == "11" {
      return Common
   } else {
      return Win
   }
}

/**
 * @Description: 税费计算函数,金额都为分
 * @param price
 * @param qty
 * @return taxPrice
 */
type TaxComputeFunc func(price int64, qty int64) (taxPrice int64)

/**
 * @Description: 税费计算策略存储处
   0为不含税 1为含税
*/
var TaxComputeFuncMap = map[int]map[string]TaxComputeFunc{
   0: map[string]TaxComputeFunc{
      Common: common,
      Win:    win,
   },
   1: map[string]TaxComputeFunc{
      Common: common,
      Win:    win,
   },
}

/**
 * @Description: 计算普通商品税费
 * @param price
 * @param qty
 * @return taxPrice
 */
func common(price int64, qty int64) (taxPrice int64) {
   radio := 0.1
   fmt.Println("计算普通商品税费")
   return int64(float64(price*qty) * radio)
}

/**
 * @Description: 计算酒类税费
 * @param price
 * @param qty
 * @return taxPrice
 */
func win(price int64, qty int64) (taxPrice int64) {
   radio := 0.2
   fmt.Println("计算普酒类税费")
   return int64(float64(price*qty) * radio)
}

/**
 * @Description: 计算税费
 * @param withTax
 * @param productType
 * @param price
 * @param qty
 */
func ComputeTaxPrice(withTax int, productType string, price int64, qty int64) {
   if taxFunc, ok := TaxComputeFuncMap[withTax][productType]; ok {
      taxPrice := taxFunc(price, qty)
      fmt.Println("税费为", taxPrice)
   } else {
      fmt.Println("输入有误,无法计算")
   }
}

func main() {
   //获取商品是否含税、商品价格、商品数量、商品类型
   withTax := 0
   var price, qty int64 = 10000, 3
   productType := getProductType("11")
   //计算税费
   ComputeTaxPrice(withTax, productType, price, qty)
}

输出:

➜ myproject go run main.go

计算普通商品税费

税费为 3000

这么写有哪些好处呢?首先代码很精简,没有一堆判断;其次无论是增加策略还是修改策略,都不会影响主框架,即main\ComputeTaxPrice,只需在TaxComputeFuncMap添加新的策略即可,很好的做到了对扩展开放,虽然对TaxComputeFuncMap有一定更改,但变动不大,能够接受。

实例

来个 🌰,我们在保存文件的时候,由于政策或者其他的原因可能需要选择不同的存储方式,敏感数据我们需要加密存储,不敏感的数据我们可以直接明文保存。

代码

package strategy

import (
	"fmt"
	"io/ioutil"
	"os"
)

// StorageStrategy 存储策略
type StorageStrategy interface {
	Save(name string, data []byte) error
}

var strategys = map[string]StorageStrategy{
	"file":         &fileStorage{},
	"encrypt_file": &encryptFileStorage{},
}

// NewStorageStrategy NewStorageStrategy
func NewStorageStrategy(t string) (StorageStrategy, error) {
	s, ok := strategys[t]
	if !ok {
		return nil, fmt.Errorf("not found StorageStrategy: %s", t)
	}

	return s, nil
}

// FileStorage 保存到文件
type fileStorage struct{}

// Save Save
func (s *fileStorage) Save(name string, data []byte) error {
	return ioutil.WriteFile(name, data, os.ModeAppend)
}

// encryptFileStorage 加密保存到文件
type encryptFileStorage struct{}

// Save Save
func (s *encryptFileStorage) Save(name string, data []byte) error {
	// 加密
	data, err := encrypt(data)
	if err != nil {
		return err
	}

	return ioutil.WriteFile(name, data, os.ModeAppend)
}

func encrypt(data []byte) ([]byte, error) {
	// 这里实现加密算法
	return data, nil
}

单元测试

package strategy

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func Test_demo(t *testing.T) {
	// 假设这里获取数据,以及数据是否敏感
	data, sensitive := getData()
	strategyType := "file"
	if sensitive {
		strategyType = "encrypt_file"
	}

	storage, err := NewStorageStrategy(strategyType)
	assert.NoError(t, err)
	assert.NoError(t, storage.Save("./test.txt", data))
}

// getData 获取数据的方法
// 返回数据,以及数据是否敏感
func getData() ([]byte, bool) {
	return []byte("test data"), false
}

总结

策略模式很好的体现了开闭原则,也说明即使是很小的优化设计,也能给项目开发带来巨大的便利。当然,这种便利会在维护的时候得到充分体现,毕竟谁维护谁知道。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是几种常见的Golang设计模式: 1. 工厂模式(Factory Pattern):用于创建对象的模式,通过定义一个创建对象的接口来实现对象的实例化。 ```go type Shape interface { Draw() } type Circle struct{} func (c *Circle) Draw() { fmt.Println("Drawing a circle") } type Rectangle struct{} func (r *Rectangle) Draw() { fmt.Println("Drawing a rectangle") } type ShapeFactory struct{} func (sf *ShapeFactory) GetShape(shapeType string) Shape { if shapeType == "circle" { return &Circle{} } else if shapeType == "rectangle" { return &Rectangle{} } return nil } func main() { factory := &ShapeFactory{} circle := factory.GetShape("circle") circle.Draw() // 输出:Drawing a circle rectangle := factory.GetShape("rectangle") rectangle.Draw() // 输出:Drawing a rectangle } ``` 2. 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。 ```go type Singleton struct{} var instance *Singleton func GetInstance() *Singleton { if instance == nil { instance = &Singleton{} } return instance } func main() { singleton1 := GetInstance() singleton2 := GetInstance() fmt.Println(singleton1 == singleton2) // 输出:true } ``` 3. 观察者模式(Observer Pattern):定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。 ```go type Subject struct { observers []Observer } func (s *Subject) Attach(observer Observer) { s.observers = append(s.observers, observer) } func (s *Subject) Notify() { for _, observer := range s.observers { observer.Update() } } type Observer interface { Update() } type ConcreteObserver struct{} func (co *ConcreteObserver) Update() { fmt.Println("Observer is updated") } func main() { subject := &Subject{} observer := &ConcreteObserver{} subject.Attach(observer) subject.Notify() // 输出:Observer is updated } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值