策略模式
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 90 课,共 107 课。
策略模式允许你定义一组算法,将每个算法封装起来,并在运行时互相替换。与多个对象响应同一个事件的观察者模式不同,策略模式让单个对象能够动态切换不同的行为。
在 Go 中,我们通过为算法定义接口,并将不同的实现注入上下文结构体,来实现 Strategy:
type PaymentStrategy interface {
Pay(amount float64) string
}
type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}
type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f via PayPal", amount)
}上下文结构体保存了对策略接口的引用,因此无需修改上下文的代码,就能替换算法:
type Checkout struct {
strategy PaymentStrategy
}
func (c *Checkout) SetStrategy(s PaymentStrategy) {
c.strategy = s
}
func (c *Checkout) ProcessPayment(amount float64) string {
return c.strategy.Pay(amount)
}现在,你可以在运行时更改支付方式:
checkout := &Checkout{}
checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00)) // 通过信用卡支付了 100.00
checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00)) // 通过 PayPal 支付了 50.00当你有多种方式来执行某项操作,并且需要根据用户输入、配置或运行时条件选择其中一种时,策略模式就很适合。常见的使用场景包括排序算法、压缩方法和验证规则。
挑战
简单一起来使用策略模式构建一个文本格式化系统吧!你将创建不同的格式化策略,并在运行时切换它们,让同一个文本处理器在不更改核心代码的情况下生成不同风格的输出。
你将把代码放在三个文件中:
formatter.go:定义策略接口和具体的格式化策略。创建一个
TextFormatter接口,其中包含方法Format(text string) string,用于按照策略规则转换文本。实现三种格式化策略:
UppercaseFormatter:将文本转换为全大写SnakeCaseFormatter:将空格转换为下划线,并将文本转换为小写TitleFormatter:将每个单词的首字母大写(单词之间用空格分隔)
processor.go:创建使用格式化策略的上下文结构体。创建一个
TextProcessor结构体,用于保存TextFormatter策略。添加以下方法:SetFormatter(f TextFormatter):更改当前使用的格式化策略Process(text string) string:使用当前的格式化器处理文本
另外,创建一个
NewTextProcessor()构造函数,返回一个尚未设置初始格式化器的处理器。main.go:演示如何在运行时切换策略。读取一段文本,然后读取格式化操作的次数。对于每次操作,读取格式类型(
upper、snake或title),为处理器设置相应的格式化器,处理原始文本,并打印结果。
输入内容如下:
- 第 1 行:要格式化的文本
- 第 2 行:格式化操作的次数
- 后续各行:每次操作的格式类型
例如,给定:
hello world today
3
upper
snake
title输出应为:
HELLO WORLD TODAY
hello_world_today
Hello World Today再例如,给定:
Go Programming Language
2
snake
upper输出应为:
go_programming_language
GO PROGRAMMING LANGUAGE再例如,给定:
design patterns are useful
1
title输出应为:
Design Patterns Are Useful注意,同一个 TextProcessor 只需切换格式化策略,就能生成完全不同的输出。处理器并不知道,也不关心当前使用的是哪种具体的格式化器。它只会调用当前设置的策略!
亲手试试
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 读取要格式化的文本
text, _ := reader.ReadString('\n')
text = strings.TrimSpace(text)
// 读取操作次数
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// 创建一个新的文本处理器
processor := NewTextProcessor()
// 处理每次格式化操作
for i := 0; i < count; i++ {
formatType, _ := reader.ReadString('\n')
formatType = strings.TrimSpace(formatType)
// TODO:根据 formatType("upper"、"snake" 或 "title"),
// 为处理器设置合适的格式化器
// 并打印处理后的文本
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器