24 种设计模式

2 阅读22分钟

创建型模式

创建型模式是一种类用于处理对象创建设计模式,主要目标是提供一种灵活的方式来创建对象,同时隐藏对象创建的具体细节,从而降低代码的耦合度,并提高代码的可复用性和可维护性

比如:http.NewRequest()bytes.NewReader()md5.New()

创建模式的核心思想是将对象的创建与使用分离,使得系统不依赖于具体的对象创建方式,而是依赖于抽象

单例模式 Singleton

确保一个类只有一个实例,并提供一个全局访问点

使用场景:

  • 配置管理
  • 日志记录
  • 数据库连接池等需要全局唯一实例的场景
package main

import (
	"fmt"
	"sync"
)

type DB struct {
	dns string
}

var db *DB
var noce sync.Once

func InitDB(dns string) *DB {
	return &DB{
		dns: dns,
	}
}
func GetDB() *DB {

	noce.Do(func() {
		db = InitDB("sss")
	})
	return db
}

func main() {
	newDb := GetDB()
	fmt.Println(newDb)
}

工厂模式 Simple Factory Pattern

将对象创建与使用分离,从而降低代码的耦合度

但具体的工厂是有区别的

简单工厂模式:

  • 特点:一个工厂类负责创建所有产品,通过条件判断决定创建哪种产品
  • 使用场景:产品种类较少,创建逻辑简单

工厂方法模式:

  • 特点:每个产品对应一个工厂类,符合开闭原则
  • 使用场景:产品种类较多,创建逻辑复杂

抽象工厂模式:

  • 每个工厂类可以创建一组相关产品,强调产品族的概念
  • 使用场景:需要创建一组相关对象的场景

简单工厂模式

简单工厂并不是一个正式的设计模式,而是一种编程习惯,通过一个工厂类来封装对象的创建逻辑,客户端只需要通过传递参数给工厂类,由工厂类决定创建哪种对象

package main

import "fmt"

type PayType int8

const (
   Ali PayType = iota
   Wx
)

type Pay interface {
   PayPage(price int64) (string, error)
}
type AliPay struct{}
type WxPay struct{}

func (AliPay) PayPage(price int64) (string, error) {
   return "支付宝支付", nil
}
func (WxPay) PayPage(price int64) (string, error) {
   return "微信支付", nil
}

func CreatePay(t PayType) Pay {
   switch t {
   case Ali:
   	return AliPay{}
   case Wx:
   	return WxPay{}
   }
   return nil

}
func main() {
   pay := CreatePay(Ali)
   result, _ := pay.PayPage(100)
   fmt.Println("支付页面:", result)
   pay2 := CreatePay(Wx)
   result2, _ := pay2.PayPage(100)
   fmt.Println("result2", result2)
}

工厂方法模式

工厂方法模式则是定义一种创建对象的接口,但将具体的创建逻辑延迟到子类中,每个子类负责创建一种具体的产品

package main

import "fmt"


type Pay interface {
   PayPage(price int64) (string, error)
}
type AliPay struct{}
type WxPay struct{}

func (AliPay) PayPage(price int64) (string, error) {
   return "支付宝支付", nil
}
func (WxPay) PayPage(price int64) (string, error) {
   return "微信支付", nil
}

type PayFactory interface {
   CreatePay() Pay
}
type AliFactory struct{}
type WxFactory struct{}

func (AliFactory) CreatePay() Pay {
   return AliPay{}
}
func (WxFactory) CreatePay() Pay {
   return WxPay{}
}
func main() {
   res1 := AliFactory{}
   a1 := res1.CreatePay()
   b1, _ := a1.PayPage(22)
   fmt.Println(b1)

   res2 := WxFactory{}
   a2 := res2.CreatePay()
   b2, _ := a2.PayPage(22)
   fmt.Println(b2)

}

抽象工厂模式

package main
import "fmt"

type PayFactory interface {
	OrderRefund() Refund
}
type AliFactory struct{}
type WxFactory struct{}

// 退款
type Refund interface {
	FactoryRefund(no string) error
}
type AliRefund struct{}
type WxRefund struct{}

func (AliFactory) OrderRefund() Refund {
	return AliRefund{}
}
func (WxFactory) OrderRefund() Refund {
	return WxRefund{}
}
func (AliRefund) FactoryRefund(no string) error {
	fmt.Println("阿里退款")
	return nil
}
func (WxRefund) FactoryRefund(no string) error {
	fmt.Println("微信退款")
	return nil
}

func main() {
	res1 := AliFactory{}
	res1.OrderRefund().FactoryRefund("")
	res2 := WxFactory{}
	res2.OrderRefund().FactoryRefund("")
}

建造者模式

它用于分布构建复杂对象,建造者模式的核心思想是将一个复杂对象的构建过程与其分离,使得同样的创建构建过程可以创建不同的表示

建造者模式试用一下场景:

  • 对象的构建过程非常复杂,包含多个步骤
  • 对象的构建过程需要支持不痛配置或表示
package main

import "fmt"

type House struct {
	Door    string
	Windows string
}
type HouseBuilder interface {
	BuildeDoor(val string)
	BuildeWindows(val string)
	GetHouse() *House
}

type Bao struct {
	house *House
}

func (b *Bao) BuildeDoor(val string) {
	b.house.Door = val
}
func (b *Bao) BuildeWindows(val string) {
	b.house.Windows = val
}
func (b *Bao) GetHouse() *House {
	return b.house
}
func NewBao() *Bao {
	return &Bao{
		house: &House{},
	}
}

type Boos struct {
	houseBulder HouseBuilder
}

func NewBoos(bao *Bao) *Boos {
	return &Boos{
		houseBulder: bao,
	}
}
func (b *Boos) GetHouse() *House {
	b.houseBulder.BuildeDoor("8888")
	b.houseBulder.BuildeWindows("999")
	return b.houseBulder.GetHouse()
}
func main() {
	bao := NewBao()
	boos := NewBoos(bao)
	fmt.Println(boos.GetHouse())
}

原型模式

通过复制现有对象来创建新对象,而不是通过新建类的方式,原型 模式的核心思想是利用对象的克隆能力,避免重复初始化,特别适用创建成本比较高的对象

使用原型模式,如果有引用类型,则需要考虑深拷贝还是浅拷贝的问题。 浅拷贝只复制对象本身而不复制其引用的对象,深拷贝则会递归地复制整个对象图,需要根据需求选择适当的拷贝方式

package main

import "fmt"

type Student struct {
	Name string
	Age  int64
}

type Prototype interface {
	Clone() Prototype
}

func (s *Student) Clone() Prototype {
	// 需要根据需求去选择拷贝方式(深浅拷贝)
	return &Student{
		Name: s.Name,
		Age:  s.Age,
	}
}

func main() {
	student1 := Student{
		Name: "xwya",
		Age:  23,
	}

	// 如果需要修改需要类型断言
	student2 := student1.Clone().(*Student)
	student2.Name = "张三"
	student2.Age = 22
	fmt.Println(student1, student2)
}

结构型模式

结构型模式,它主要关注如何将类或者对象组合成更大的结构,在不改变原有类或对象的情况下,实现新的功能或优化系统结构

结构型模式的核心思想是通过组合,而不是继承来实现代码的复用和扩展,它们帮助开发者设计出灵活,可扩展的系统结构,同时降低类与类之间的耦合度

代理模式

它通过提供一个代理对象来控制对另一个对象的访问,代理模式的核心思想是在不改变原始对象的情况下,通过代理对象来增强或限制原始对象的访问

代理模式通常用于以下场景:

  • 延迟初始化:当对象创建成本比较高时,可以通过代理延迟对象的初始化
  • 访问控制:通过代理对象限制对原始对象的访问权限
  • 日志记录: 通过代理对象记录对原始对象的访问日志
  • 缓存: 通过代理对象缓存原始对象的结果,避免重复计算
package main

import "fmt"

type Log struct {
}

func (Log) Info() {
	fmt.Println("log ...")
}

type ProxyLog struct {
	log *Log
}

func (pl *ProxyLog) Info() {
	//  代理前
	// 延迟初始化
	if pl.log == nil {
		pl.log = &Log{}
	}
	pl.log.Info()

	// 代理后
	fmt.Println("代理后")

}

func main() {
	proxy := ProxyLog{}
	proxy.Info()
}

桥接模式

它的核心思想时将抽象部分与实现部分分离,使它们可以独立变化,通过这种方式,桥接模式能够避免类的数量爆炸(即使类的组合是指数增长),同时提高代码的可扩展性和可维护性

package main

import "fmt"

type Printer interface {
	PrintFile(file string)
}
type Epson struct{}

func (Epson) PrintFile(file string) {
	fmt.Println(file, "使用爱普生打印机...")
}

type Hp struct{}

func (Hp) PrintFile(file string) {
	fmt.Println(file, "使用惠普打印机...")
}

type Computer interface {
	Print(file string)
	SetPrinter(printer Printer)
}
type Mac struct {
	printer Printer
}

func (m *Mac) Print(file string) {
	m.printer.PrintFile(file)
}
func (m *Mac) SetPrinter(printer Printer) {
	m.printer = printer
}

type Wind struct {
	printer Printer
}

func (w *Wind) Print(file string) {
	w.printer.PrintFile(file)
}
func (w *Wind) SetPrinter(printer Printer) {
	w.printer = printer
}
func main() {
	mac := Mac{}
	mac.SetPrinter(Epson{})
	mac.Print("mac电脑")
	wind := Wind{}
	wind.SetPrinter(Hp{})
	wind.Print("windows电脑")
}

组合模式

它允许你将对象组合成树形结构来表示"部分-整体"的层次关系,组合模式让客户端可以统一地处理单个对象和对象组合

应用场景:

  1. 文件系统:如目录和文件的管理,可以通过组合模式将文件夹视为组合节点,文件视为叶子节点
  2. 组织结构:如公司内部的部门和员工关系,可以通过组合模式将部门视为组合节点,员工视为叶子节点
package main

import "fmt"

type Node interface {
	Display(ident string)
}
type File struct {
	Name string
}

func (f *File) Display(ident string) {
	fmt.Println(ident + f.Name)
}

type Dir struct {
	Name     string
	Children []Node
}

func (d *Dir) Display(ident string) {
	fmt.Println(ident + d.Name)

	for _, child := range d.Children {
		child.Display(ident + "-")
	}
}

func main() {
	root := Dir{
		Name: "xwya",
		Children: []Node{
			&Dir{Name: "xwya-1", Children: []Node{
				&File{Name: "xwya-1-1.go"},
				&File{Name: "xwya-1-2.go"},
			}},
			&File{Name: "xwya1.go"},
		},
	}
	root.Display("-")
}
	```

## 装饰器模式

它允许动态地为对象添加行为或职责,而不需要修改对象的原始类,通过引入装饰器类,可以在运行时灵活地组合不同的功能,而不需要创建大量的子类,装饰器模式的核心思想是将对象包装在一个或多个装饰者中,每个装饰者都可以在调用被装饰对象的方法之前或之后额外添加行为

1. 日志记录:记录每个请求的详细信息
2. 性能监控: 记录每个请求的处理事件

```go
package main

import "fmt"

type ReqI interface {
	Handler(url string)
}
type Req struct{}

func (r Req) Handler(url string) {
	fmt.Println("请求中...." + url)
}

type LogReqDecorator struct {
	req ReqI
}

func (l *LogReqDecorator) Handler(url string) {
	fmt.Println("请求前...." + url)
	l.req.Handler(url)
	fmt.Println("请求完成" + url)

}

func main() {
	req := Req{}
	req2 := LogReqDecorator{req: req}
	req2.Handler("baiwu.com")
}

适配器模式

它允许不兼容的接口之间进行协作。适配器模式的核心思想是将一个类的接口转换成客户端期望的另一个接口,从而使原本不兼容的类能够一起工作

假设你的系统需要集成一个第三方支付系统,但接口与你的系统不兼容,你可以使用适配器模拟将第三方支付系统的接口转换为你的系统期望的接口

package main

import "fmt"

type AliPay struct{}

func (a *AliPay) GetPayPage() string {
	return "支付宝连接"
}

type WxPay struct{}

func (w *WxPay) PayPage() string {
	return "微信连接"
}

type AliPayB struct {
	aliPay *AliPay
}

func (a *AliPayB) PayPage() string {
	return a.aliPay.GetPayPage()
}

type PayI interface {
	PayPage() string
}

func PayPage(pi PayI) string {
	return pi.PayPage()
}

func main() {
	fmt.Println(PayPage(&WxPay{}))
	fmt.Println(PayPage(&AliPayB{aliPay: &AliPay{}}))
}

外观模式

它提供一个统一的接口,用于访问子系统中的一组接口。外观模式的核心思想是简化复杂系统的使用,通过提供一个高层接口,隐藏系统的复杂性,使客户端更容易使用

享元模式

通过共享对象来减少内存使用和提高性能,享元模式的核心思想是将对象的共享部分(内部状态) 与不可共享部门(外部状态)分离,从而减少重复对象的创建

共享模式的核心思想:

  1. 共享对象:享元模式通过共享相同的内部状态来减少内存使用
  2. 分离状态:将对象的状态分为内部状态(可共享)和外部状态(不可共享)
  3. 工厂管理: 使用工厂模式来管理和复用享元对象
package main

import (
	"fmt"
	"sync"
)

// 享元对象接口
type Flyweight interface {
	Operate(extrinsicState string)
}

// 具体享元对象
type ConcreteFlyweight struct {
	intrinsicState string // 内部状态
	mu              sync.Mutex // 使用Mutex加锁
}

// 操作方法,包含临界区
func (c *ConcreteFlyweight) Operate(extrinsicState string) {
	// 获取锁
	c.mu.Lock()
	defer c.mu.Unlock() // 确保函数退出时释放锁

	// 访问共享资源
	fmt.Printf("Character: %s, with external state: %s\n", c.intrinsicState, extrinsicState)
}

// 享元工厂,管理享元对象
type FlyweightFactory struct {
	flyweights map[string]*ConcreteFlyweight
	mu         sync.Mutex
}

// 获取享元对象
func (f *FlyweightFactory) GetFlyweight(key string) *ConcreteFlyweight {
	f.mu.Lock()
	defer f.mu.Unlock()

	// 如果对象已存在,则返回共享对象
	if _, exists := f.flyweights[key]; exists {
		return f.flyweights[key]
	}

	// 否则,创建新对象并存入map中
	flyweight := &ConcreteFlyweight{intrinsicState: key}
	f.flyweights[key] = flyweight
	return flyweight
}

func main() {
	// 创建享元工厂
	factory := &FlyweightFactory{flyweights: make(map[string]*ConcreteFlyweight)}

	// 获取共享的享元对象
	flyweight1 := factory.GetFlyweight("A")
	flyweight2 := factory.GetFlyweight("B")
	flyweight3 := factory.GetFlyweight("A") // 重复获取相同的对象

	// 并发操作
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		flyweight1.Operate("State 1")
	}()

	go func() {
		defer wg.Done()
		flyweight2.Operate("State 2")
	}()

	wg.Wait()

	// 打印出flyweight1和flyweight3应该是同一个对象
	fmt.Printf("flyweight1 and flyweight3 are same object: %v\n", flyweight1 == flyweight3)
}


行为型模式

行为行模式,主要关注对象之间的职责分配和通信方式。行为型模式的核心思想是通过定义对象之间的交互方式,来更好的实现系统的功能,同时降低对象之间的耦合度

责任链模式

责任链模式(Chain of Responsibility)是一种行为设计模式,它允许多个对象有机会处理请求,从而避免请求的发送者与接收者之间的耦合,责任链模式将这些对象连成一条链,并沿着这条链传递请求,直到有对象处理他们为止

核心思想:

  1. 解耦请求发送者和接收者:请求的发送者不需要知道具体由哪个对象处理请求,只需要将请求发送到链上即可
  2. 动态组合处理逻辑:可以动态地组合处理者,灵活的调整处理顺序或增加新的处理者。
  3. 每个处理者只关心自己的职责,每个处理者只处理自己能处理的请求,不能处理的请求会传递给下一个处理者

适用场景:

  • 当一个请求需要经过多个对象处理,但具体由哪个对象处理不确定时
  • 当需要动态指定处理请求的对象时
  • 当希望避免请求发送者与接收者之间的紧密耦合时

实际应用场景:

  • 中间件
  • 审批流程

责任链模式的优点:

  • 降低了请求发送者与接收者之间的耦合。
  • 增加或修改处理请求的行为时,不需要修改客户端代码。
  • 每个处理者只关注自己能够处理的请求,其他请求交给链条中的下一个处理者。
package main

import (
	"fmt"
	"net/http"
)

type Context struct {
	request  *http.Request
	w        *http.ResponseWriter
	index    int
	handlers []HandlerFun
}

func (c *Context) Next() {
	c.index++
	if c.index < len(c.handlers) {
		c.handlers[c.index](c)
	}
}

func (c *Context) About() {
	c.index = len(c.handlers)
}

type HandlerFun func(*Context)
type Engine struct {
	handlers []HandlerFun
}

func (e *Engine) Use(f HandlerFun) {
	e.handlers = append(e.handlers, f)
}
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	content := &Context{
		request:  r,
		w:        &w,
		handlers: e.handlers,
		index:    -1,
	}
	content.Next()
}
func AuthMiddleware(c *Context) {
	fmt.Println("处理i请求1.。。")
	// c.Next()
}
func LogMiddleWare(c *Context) {
	fmt.Println("处理i请求2.。。")
	// c.Next()
}
func main() {
	r := &Engine{}
	r.Use(LogMiddleWare)
	r.Use(AuthMiddleware)
	http.ListenAndServe(":8080", r)
}

命令模式

它将封装为一个对象,从而使你可以用不同的请求对客户进行参数化,并且支持请求的排队,记录日志,撤销操作等功能

核心思想:

  1. 将请求封装为对象:将每个请求(如方法调用)封装为独立的对象
  2. 解耦请求的发送者和接收者:发送者不需要知道接收者的具体实现,只需要通过命令对象来执行请求
  3. 支持扩展:可以轻松的添加新的命令,而不需要修改现有代码
package main

import (
	"fmt"
)

type Command interface {
	Execute()
}
type sendNews struct {
	Context string
}

func (s *sendNews) Execute() {
	fmt.Println("发布新闻", s.Context)
}

type sendEmail struct {
	Context string
	To      string
	Form    string
}

func (s *sendEmail) Execute() {
	fmt.Println(s.To, "发送消息:", s.Form, "给", s.Context)
}

type RemoteControl struct {
	command []Command
}

func (r *RemoteControl) Add(c Command) {
	r.command = append(r.command, c)
}
func (r *RemoteControl) exec() {
	for _, e := range r.command {
		e.Execute()
	}
}

func NewControl() *RemoteControl {
	return &RemoteControl{}
}
func main() {
	r := NewControl()
	r.Add(&sendEmail{
		Context: "你好你好",
		To:      "小红",
		Form:    "小明",
	})
	r.Add(&sendNews{
		Context: "内容内容你好",
	})
	r.exec()
}

解释器模式

它定义了一种语言的语法表示,并提供了一个解释器来解释这种语法,解释器模式通常用于处理类似编程语言,查询语言,规则引擎等场景

核心思想:

  1. 定义语法规则:将语言的语法规则表示为一个抽象的语法树(AST).
  2. 解释执行:通过解释器遍历抽象语法树,执行相应的操作
package main

import (
	"bytes"
	"fmt"
	"strings"
)

// Context 表示用于替换占位符的数据
type Context map[string]interface{}

// Interpreter 解释器接口
type Interpreter interface {
	Interpret(ctx Context) string
}

// TextNode 表示普通文本节点
type TextNode struct {
	Text string
}

func (t *TextNode) Interpret(ctx Context) string {
	return t.Text
}

// PlaceholderNode 表示占位符节点
type PlaceholderNode struct {
	Field string
}

func (p *PlaceholderNode) Interpret(ctx Context) string {
	if value, exists := ctx[p.Field]; exists {
		return fmt.Sprintf("%v", value)
	}
	return ""
}

// TemplateParser 模板解析器
type TemplateParser struct {
	tmpl string
}

func NewTemplateParser(tmpl string) *TemplateParser {
	return &TemplateParser{tmpl: tmpl}
}

// Parse 解析模板并返回节点列表
func (p *TemplateParser) Parse() []Interpreter {
	var nodes []Interpreter
	var buffer bytes.Buffer
	inPlaceholder := false

	for _, char := range p.tmpl {
		switch char {
		case '{':
			if buffer.Len() > 0 && !inPlaceholder {
				nodes = append(nodes, &TextNode{Text: buffer.String()})
				buffer.Reset()
			}
			inPlaceholder = true
			buffer.WriteRune(char)
		case '}':
			buffer.WriteRune(char)
			if inPlaceholder && strings.HasSuffix(buffer.String(), "}}") {
				field := strings.TrimPrefix(strings.TrimSuffix(buffer.String(), "}}"), "{{.")
				nodes = append(nodes, &PlaceholderNode{Field: field})
				buffer.Reset()
				inPlaceholder = false
			}
		default:
			buffer.WriteRune(char)
		}
	}

	// 处理剩余的文本
	if buffer.Len() > 0 {
		nodes = append(nodes, &TextNode{Text: buffer.String()})
	}

	return nodes
}

// Execute 执行模板解释
func Execute(nodes []Interpreter, ctx Context) string {
	var result strings.Builder
	for _, node := range nodes {
		result.WriteString(node.Interpret(ctx))
	}
	return result.String()
}

func main() {
	tmpl := "Hello, {{.Name}}! You are {{.Age}} years old.\n"
	parser := NewTemplateParser(tmpl)
	nodes := parser.Parse()

	// 定义上下文数据
	ctx := Context{
		"Name": "Alice",
		"Age":  30,
	}

	result := Execute(nodes, ctx)
	fmt.Println(result)
}

go/template 包使用:

package main
import (
	"os"
	"text/template"
)

type Person struct {
	Name string
	Age  int
}

func main() {

	// 定义模板
	const tmpl = "Hello, {{.Name}}! You are {{.Age}} years old.\n"

	// 解析模板
	t, err := template.New("example").Parse(tmpl)
	if err != nil {
		panic(err)
	}

	// 数据
	person := Person{Name: "Alice", Age: 30}

	// 执行模板,输出到标准输出
	err = t.Execute(os.Stdout, person)
	if err != nil {
		panic(err)
	}
}

迭代器模式

它一共了一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示,迭代器模式的核心思想是将遍历逻辑从聚合对象中分离出来,使得聚合对象和遍历对象逻辑可以独立变化

  1. 集合类库:比如goslice,map等集合类型,可以通过迭代器模式提供统一的遍历接口
  2. 数据库查询结果:数据库查询结果可以封装为一个聚合对象,并提供迭代器遍历查询结果
  3. 文件系统遍历:文件系统中的目录和文件可以封装为一个聚合对象,并提供迭代器遍历查询结果
  4. 树形结构遍历:树形结构(如二叉型,多叉型)可以提供多种遍历方式(如深度优先,广度优先)
package main

import "fmt"

// Iterator 迭代器接口
type Iterator interface {
	Next() int       // 返回下一个元素
	HasNext() bool   // 检查是否还有更多元素
}

// IntSliceIterator 整数切片的具体迭代器
type IntSliceIterator struct {
	data []int
	pos  int
}

// Next 返回下一个元素并移动位置
func (it *IntSliceIterator) Next() int {
	if it.HasNext() {
		value := it.data[it.pos]
		it.pos++
		return value
	}
	return 0 // 没有更多元素时返回 0,实际可根据需求处理
}

// HasNext 检查是否还有更多元素
func (it *IntSliceIterator) HasNext() bool {
	return it.pos < len(it.data)
}

// IntSliceAggregate 整数切片的集合
type IntSliceAggregate struct {
	data []int
}

// CreateIterator 创建并返回一个具体的迭代器
func (agg *IntSliceAggregate) CreateIterator() Iterator {
	return &IntSliceIterator{
		data: agg.data,
		pos:  0,
	}
}

func main() {
	// 创建整数切片集合
	aggregate := &IntSliceAggregate{
		data: []int{1, 2, 3, 4, 5},
	}

	// 创建迭代器
	iterator := aggregate.CreateIterator()

	// 使用迭代器遍历集合
	for iterator.HasNext() {
		fmt.Println(iterator.Next())
	}
}

中介者模式

通过引入一个中介对象来封装一组对象之间的交互,中介者模式的核心思想是将对象之间的复杂交互集中到一个中介对象中,从而减少对象之间的直接耦合

package main

import "fmt"

// Mediator 中介者接口
type Mediator interface {
	SendMessage(message string, sender Colleague)
	Register(colleague Colleague)
}

// Colleague 同事类接口
type Colleague interface {
	ReceiveMessage(message string)
	SetMediator(mediator Mediator)
	Send(message string)
	GetName() string
}

// ChatMediator 具体中介者,协调各同事之间的交互
type ChatMediator struct {
	colleagues []Colleague
}

// Register 注册同事类
func (m *ChatMediator) Register(colleague Colleague) {
	m.colleagues = append(m.colleagues, colleague)
}

// SendMessage 通过中介者发送消息
func (m *ChatMediator) SendMessage(message string, sender Colleague) {
	for _, colleague := range m.colleagues {
		if colleague != sender { // 不向发送者发送消息
			colleague.ReceiveMessage(fmt.Sprintf("%s: %s", sender.GetName(), message))
		}
	}
}

// User 具体同事类
type User struct {
	name     string
	mediator Mediator
}

// NewUser 创建一个用户实例
func NewUser(name string) *User {
	return &User{name: name}
}

// SetMediator 设置中介者
func (u *User) SetMediator(mediator Mediator) {
	u.mediator = mediator
}

// Send 发送消息
func (u *User) Send(message string) {
	fmt.Printf("%s sends: %s\n", u.name, message)
	u.mediator.SendMessage(message, u)
}

// ReceiveMessage 接收消息
func (u *User) ReceiveMessage(message string) {
	fmt.Printf("%s receives: %s\n", u.name, message)
}

// GetName 返回用户名
func (u *User) GetName() string {
	return u.name
}

func main() {
	// 创建中介者
	mediator := &ChatMediator{}

	// 创建用户并注册到中介者
	user1 := NewUser("Alice")
	user2 := NewUser("Bob")
	user3 := NewUser("Charlie")

	user1.SetMediator(mediator)
	user2.SetMediator(mediator)
	user3.SetMediator(mediator)

	mediator.Register(user1)
	mediator.Register(user2)
	mediator.Register(user3)

	// 用户发送消息
	user1.Send("Hello, everyone!")
	user2.Send("Hi Alice!")
	user3.Send("Hey folks!")
}

备忘录模式

它允许在不被破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便稍后可以将该对象恢复到之前的状态,备忘录模式的核心思想:将对象的状态保存到一个备忘录对象中,并在需要的时候从备忘录中恢复状态

package main

import "fmt"

// Memento 备忘录,保存编辑器的状态
type Memento struct {
	content string
}

// Editor 发起人,提供保存和恢复状态的方法
type Editor struct {
	content string
}

// CreateMemento 创建备忘录保存当前状态
func (e *Editor) CreateMemento() *Memento {
	return &Memento{content: e.content}
}

// RestoreMemento 使用备忘录恢复状态
func (e *Editor) RestoreMemento(m *Memento) {
	e.content = m.content
}

// SetContent 设置新的内容
func (e *Editor) SetContent(content string) {
	e.content = content
}

// GetContent 获取当前内容
func (e *Editor) GetContent() string {
	return e.content
}

// Caretaker 管理者,保存备忘录历史记录
type Caretaker struct {
	history []*Memento
}

// AddMemento 添加备忘录到历史记录
func (c *Caretaker) AddMemento(m *Memento) {
	c.history = append(c.history, m)
}

// GetMemento 获取指定位置的备忘录
func (c *Caretaker) GetMemento(index int) *Memento {
	if index < len(c.history) {
		return c.history[index]
	}
	return nil
}

func main() {
	editor := &Editor{}
	caretaker := &Caretaker{}

	// 设置初始内容并保存状态
	editor.SetContent("Hello, world!")
	caretaker.AddMemento(editor.CreateMemento())
	fmt.Println("Content:", editor.GetContent())

	// 修改内容并保存状态
	editor.SetContent("Hello, Go!")
	caretaker.AddMemento(editor.CreateMemento())
	fmt.Println("Content:", editor.GetContent())

	// 再次修改内容
	editor.SetContent("Hello, design patterns!")
	fmt.Println("Content:", editor.GetContent())

	// 撤销到上一个状态
	editor.RestoreMemento(caretaker.GetMemento(1))
	fmt.Println("After Undo:", editor.GetContent())

	// 撤销到最初状态
	editor.RestoreMemento(caretaker.GetMemento(0))
	fmt.Println("After Undo:", editor.GetContent())
}

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,当对象间存在一对多的依赖关系时,使用该模式可以使得一个对象的状态发生改变时,其依赖者(观察者)会收到通知并自动更新。

package main

import "fmt"

// Observer 观察者接口,定义更新方法
type Observer interface {
	Update(price float64)
}

// Stock 被观察者,维护股票价格和观察者列表
type Stock struct {
	observers []Observer
	price     float64
}

// Attach 添加观察者
func (s *Stock) Attach(observer Observer) {
	s.observers = append(s.observers, observer)
}

// Detach 移除观察者
func (s *Stock) Detach(observer Observer) {
	for i, o := range s.observers {
		if o == observer {
			s.observers = append(s.observers[:i], s.observers[i+1:]...)
			break
		}
	}
}

// Notify 通知所有观察者
func (s *Stock) Notify() {
	for _, observer := range s.observers {
		observer.Update(s.price)
	}
}

// SetPrice 设置股票价格并通知观察者
func (s *Stock) SetPrice(price float64) {
	s.price = price
	s.Notify()
}

// Investor 具体观察者,接收到通知后进行操作
type Investor struct {
	name string
}

// Update 更新方法,接收股票价格变动通知
func (i *Investor) Update(price float64) {
	fmt.Printf("Investor %s notified: New stock price is %.2f\n", i.name, price)
}

func main() {
	// 创建被观察者
	stock := &Stock{}

	// 创建观察者
	investor1 := &Investor{name: "Alice"}
	investor2 := &Investor{name: "Bob"}

	// 注册观察者
	stock.Attach(investor1)
	stock.Attach(investor2)

	// 改变股票价格,通知观察者
	stock.SetPrice(100.0)
	stock.SetPrice(105.5)

	// 移除一个观察者
	stock.Detach(investor1)

	// 再次改变股票价格,通知剩余的观察者
	stock.SetPrice(110.0)
}

状态模式

它允许一个对象在内部状态发生改变时改变其行为,对象看起来好像修改了其类。通过将状态的行为封装到不同的状态类中,状态模式可以避免大量的 if-elseswitch 语句

package main

import "fmt"

// State 状态接口
type State interface {
	Publish(context *Document) // 发布操作
	Reject(context *Document)  // 拒绝操作
}

// Document 上下文,维护当前状态
type Document struct {
	state State
}

// SetState 设置文档的当前状态
func (d *Document) SetState(state State) {
	d.state = state
}

// Publish 请求发布操作
func (d *Document) Publish() {
	d.state.Publish(d)
}

// Reject 请求拒绝操作
func (d *Document) Reject() {
	d.state.Reject(d)
}

// DraftState 草稿状态
type DraftState struct{}

func (s *DraftState) Publish(context *Document) {
	fmt.Println("Document is now under review.")
	context.SetState(&ReviewState{})
}

func (s *DraftState) Reject(context *Document) {
	fmt.Println("Draft cannot be rejected.")
}

// ReviewState 审核中状态
type ReviewState struct{}

func (s *ReviewState) Publish(context *Document) {
	fmt.Println("Document is now published.")
	context.SetState(&PublishedState{})
}

func (s *ReviewState) Reject(context *Document) {
	fmt.Println("Document rejected. Returning to draft.")
	context.SetState(&DraftState{})
}

// PublishedState 已发布状态
type PublishedState struct{}

func (s *PublishedState) Publish(context *Document) {
	fmt.Println("Document is already published.")
}

func (s *PublishedState) Reject(context *Document) {
	fmt.Println("Published document cannot be rejected.")
}

func main() {
	doc := &Document{state: &DraftState{}} // 初始化为草稿状态

	// 执行一系列状态变更操作
	doc.Publish() // 草稿 -> 审核中
	doc.Publish() // 审核中 -> 已发布
	doc.Reject()  // 已发布状态,拒绝无效
	doc.Reject()  // 再次拒绝无效
}

策略模式

策略模式通过将不同的算法封装到独立的策略类中,使得它们可以互相替换。客户端可以在运行时根据需要选择不同的策略,而不需要修改上下文类。

package main

import "fmt"

// Strategy 策略接口
type PaymentStrategy interface {
	Pay(amount float64)
}

// CreditCardPayment 具体策略 - 信用卡支付
type CreditCardPayment struct{}

func (c *CreditCardPayment) Pay(amount float64) {
	fmt.Printf("Paid %.2f using Credit Card.\n", amount)
}

// AlipayPayment 具体策略 - 支付宝支付
type AlipayPayment struct{}

func (a *AlipayPayment) Pay(amount float64) {
	fmt.Printf("Paid %.2f using Alipay.\n", amount)
}

// WeChatPayment 具体策略 - 微信支付
type WeChatPayment struct{}

func (w *WeChatPayment) Pay(amount float64) {
	fmt.Printf("Paid %.2f using WeChat Pay.\n", amount)
}

// PaymentContext 上下文 - 支付上下文
type PaymentContext struct {
	strategy PaymentStrategy
}

// SetStrategy 设置支付策略
func (p *PaymentContext) SetStrategy(strategy PaymentStrategy) {
	p.strategy = strategy
}

// ExecutePayment 执行支付
func (p *PaymentContext) ExecutePayment(amount float64) {
	p.strategy.Pay(amount)
}

func main() {
	// 创建上下文
	paymentContext := &PaymentContext{}

	// 设置为信用卡支付并执行支付
	paymentContext.SetStrategy(&CreditCardPayment{})
	paymentContext.ExecutePayment(100.0)

	// 设置为支付宝支付并执行支付
	paymentContext.SetStrategy(&AlipayPayment{})
	paymentContext.ExecutePayment(200.0)

	// 设置为微信支付并执行支付
	paymentContext.SetStrategy(&WeChatPayment{})
	paymentContext.ExecutePayment(300.0)
}

模板方法模式

模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个算法的骨架,并允许子类在不改变算法结构的情况下重新定义算法的某些步骤。模板方法模式通过将算法的实现步骤分离到子类中,从而使得子类可以在不改变整体流程的情况下定制算法的某些部分。

package main

import "fmt"

// TeaTemplate 抽象类,定义泡茶的模板方法
type TeaTemplate interface {
	BoilWater()
	Brew()
	PourInCup()
	AddCondiments()
	PrepareTea() // 模板方法
}

// PrepareTea 模板方法,定义泡茶的固定流程
func (t *TeaTemplate) PrepareTea() {
	t.BoilWater()      // 步骤 1:烧水
	t.Brew()           // 步骤 2:泡茶
	t.PourInCup()      // 步骤 3:倒入杯中
	t.AddCondiments()  // 步骤 4:添加调味品
}

// GreenTea 具体类,表示绿茶泡法
type GreenTea struct{}

func (g *GreenTea) BoilWater() {
	fmt.Println("Boiling water for green tea.")
}

func (g *GreenTea) Brew() {
	fmt.Println("Steeping the green tea.")
}

func (g *GreenTea) PourInCup() {
	fmt.Println("Pouring green tea into the cup.")
}

func (g *GreenTea) AddCondiments() {
	fmt.Println("Adding lemon to the green tea.")
}

// BlackTea 具体类,表示红茶泡法
type BlackTea struct{}

func (b *BlackTea) BoilWater() {
	fmt.Println("Boiling water for black tea.")
}

func (b *BlackTea) Brew() {
	fmt.Println("Steeping the black tea.")
}

func (b *BlackTea) PourInCup() {
	fmt.Println("Pouring black tea into the cup.")
}

func (b *BlackTea) AddCondiments() {
	fmt.Println("Adding milk to the black tea.")
}

func main() {
	// 创建绿茶和红茶的对象,并通过模板方法泡茶
	var tea TeaTemplate

	tea = &GreenTea{}
	tea.PrepareTea()

	fmt.Println()

	tea = &BlackTea{}
	tea.PrepareTea()
}

访问者模式

它让我们可以在不修改类的情况下,向已有的类添加新的功能。该模式通过将操作封装到访问者对象中,使得类的元素可以通过访问者对象来接收操作,并执行相应的行为。

访问者模式主要用于处理复杂对象结构(如对象集合、树形结构等)的访问操作。每次要对这些对象进行操作时,我们不会修改它们的结构,而是通过新增不同的访问者来扩展操作。

package main

import "fmt"

// Visitor 访问者接口,定义对每个元素的访问方法
type Visitor interface {
	VisitBook(book *Book)
	VisitFood(food *Food)
}

// Element 元素接口,定义接受访问者的方法
type Element interface {
	Accept(visitor Visitor)
}

// Book 具体元素 - 书籍
type Book struct {
	Price float64
}

func (b *Book) Accept(visitor Visitor) {
	visitor.VisitBook(b)
}

// Food 具体元素 - 食品
type Food struct {
	Price float64
}

func (f *Food) Accept(visitor Visitor) {
	visitor.VisitFood(f)
}

// ConcreteVisitor 具体访问者 - 计算总价的访问者
type ConcreteVisitor struct {
	TotalPrice float64
}

func (v *ConcreteVisitor) VisitBook(book *Book) {
	v.TotalPrice += book.Price
}

func (v *ConcreteVisitor) VisitFood(food *Food) {
	v.TotalPrice += food.Price
}

// ObjectStructure 购物车结构,包含元素
type ObjectStructure struct {
	Items []Element
}

func (os *ObjectStructure) Accept(visitor Visitor) {
	for _, item := range os.Items {
		item.Accept(visitor)
	}
}

func main() {
	// 创建购物车并添加商品
	book := &Book{Price: 30.0}
	food := &Food{Price: 10.0}
	cart := &ObjectStructure{
		Items: []Element{book, food},
	}

	// 创建具体访问者并计算总价
	visitor := &ConcreteVisitor{}
	cart.Accept(visitor)

	fmt.Printf("Total Price: %.2f\n", visitor.TotalPrice)
}