行为型之策略模式

策略模式

策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的。

工厂模式是解耦对象的创建和使用,观察者模式是解耦观察者和被观察者。策略模式跟两者类似,也能起到解耦的作用,不过,它解耦的是策略的定义、创建、使用这三部分。

在这里插入图片描述

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

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
}

详细版例子

// 策略模式:定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)
// 解耦策略的定义、创建、使用
// 面向接口编程
package main

import (
	"errors"
	"fmt"
)

/** 1. 策略定义 */
type Strategy interface {
	algorithmInterface()
}

// class ConcreteStrategyA
type ConcreteStrategyA struct{}

func NewConcreteStrategyA() ConcreteStrategyA {
	return ConcreteStrategyA{}
}

func (c ConcreteStrategyA) algorithmInterface() {
	// 具体的算法...
	fmt.Println("algorithm A")
}

// class ConcreteStrategyB
type ConcreteStrategyB struct{}

func NewConcreteStrategyB() ConcreteStrategyB {
	return ConcreteStrategyB{}
}

func (c ConcreteStrategyB) algorithmInterface() {
	// 具体的算法...
	fmt.Println("algorithm B")
}

/** 2. 策略创建 */

// 方式一:针对无状态策略类,不需要每次都实时创建
type StrategyFactory struct {
	strategies map[string]Strategy
}

func NewStrategyFactory() StrategyFactory {
	strategies := make(map[string]Strategy)
	strategies["A"] = NewConcreteStrategyA()
	strategies["B"] = NewConcreteStrategyB()
	return StrategyFactory{strategies: strategies}
}

func (sf StrategyFactory) getStrategy(strategyType string) (Strategy, error) {
	if strategyType == "" {
		return nil, errors.New("type should not be empty")
	}
	// 查表法
	strategy, ok := sf.strategies[strategyType]
	if !ok {
		return nil, errors.New("strategyType is invalid")
	}
	return strategy, nil
}

// 方式二:有状态策略类,每次都需要创建新的策略对象
type StrategyFactory2 struct{}

func NewStrategyFactoryV2() StrategyFactory2 {
	return StrategyFactory2{}
}

func (sf StrategyFactory2) getStrategy(strategyType string) (Strategy, error) {
	if strategyType == "" {
		return nil, errors.New("type should not be empty")
	}
	if strategyType == "A" {
		return NewConcreteStrategyA(), nil
	}
	if strategyType == "B" {
		return NewConcreteStrategyB(), nil
	}
	return nil, errors.New("strategyType is invalid")
}

/** 3. 策略使用 */
func Demo() {
	sf := NewStrategyFactory()

	strategyType := getStrategyType()
	// 获取策略,这一步通过修改 strategyType,可以灵活替换策略
	strategy, err := sf.getStrategy(strategyType)
	if err != nil {
		panic("load strategy failed")
	}
	strategy.algorithmInterface()

}

// A、B 可能从配置或参数中读取
func getStrategyType() string {
	return "A"
}

如果光从代码来看,策略模式和工厂模式有一些相似,只是多了策略使用这一部分,但要牢记一点,设计模式是和场景绑定的。策略模式面向的是算法类的定义、创建和使用,比如不同促销活动下的价格计算、不同场景下的排序算法选择等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值