软件设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它的意义在于这些模式是众多程序员经过相当长的一段时间的试验和错误复盘所总结的宝贵经验,可以帮助我们提高代码的可重用性、可读性和可靠性。本文由腾讯云天御业务安全工程师王顺驰撰写,总结了 10 种经典的软件设计模式的特点、优缺点和应用场景,并给出了相应代码示例。
关注腾讯云开发者,一手技术干货提前解锁👇
单例模式是一种创建型设计模式,它限制了实例化类的对象个数,确保在任何情况下,一个类只有一个实例,并且提供一个全局访问点。这种模式在需要全局状态控制或共享资源访问时非常有用。
特点:
优点:
缺点:
应用场景:
// 定义一个结构体Singleton,用于存储单例的实例数据
type singleton struct {
value string // 这里可以存储单例对象的任何数据
}
// 定义一个全局变量instance,用于存储单例的实例
var instance *singleton
// getInstance函数用于获取单例的实例
// 如果instance为nil,则创建一个新的singleton实例
// 否则,返回已存在的实例
func getInstance() *singleton {
if instance == nil {
instance = &singleton{value: "unique instance"} // 这里初始化singleton实例
}
return instance // 返回单例的实例
}
func main() {
// 获取单例的实例
singletonInstance := getInstance()
fmt.Println(singletonInstance.value) // 输出: unique instance
// 再次获取单例的实例,将返回相同的实例
anotherInstance := getInstance()
if singletonInstance == anotherInstance {
fmt.Println("Both instances are the same") // 输出: Both instances are the same
}
}
在并发环境下如果没有适当的同步机制,多个 goroutine 可能会同时检测到instance
为nil
并尝试创建新的实例,从而导致创建多个实例。为了解决这个问题,可以使用sync.Once
,它确保在并发环境中只执行一次初始化操作。
// 定义一个结构体Singleton,用于存储单例的实例数据
type singleton struct {
value string // 这里可以存储单例对象的任何数据
}
// 定义一个Once对象,用于确保初始化操作只执行一次
var once sync.Once
// 定义一个全局变量instance,用于存储单例的实例
var instance *singleton
// 初始化函数,由Once.Do调用
func initSingleton() {
instance = &singleton{value: "unique instance"} // 这里初始化singleton实例
}
// getInstance函数用于获取单例的实例
func getInstance() *singleton {
// 执行initSingleton,确保instance只被初始化一次
once.Do(initSingleton)
return instance // 返回单例的实例
}
func main() {
// 获取单例的实例
singletonInstance := getInstance()
fmt.Println(singletonInstance.value) // 输出: unique instance
// 再次获取单例的实例,将返回相同的实例
anotherInstance := getInstance()
if singletonInstance == anotherInstance {
fmt.Println("Both instances are the same") // 输出: Both instances are the same
}
// 测试并发环境下的单例模式
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
singletonInstance := getInstance()
fmt.Println(singletonInstance.value)
}()
}
wg.Wait()
}
工厂模式是一种创建型设计模式,用于将对象的创建过程封装起来,由子类决定实例化哪一个类。这种模式使得代码结构更加清晰,并且能够轻松替换或扩展产品类。
特点:
优点:
缺点:
应用场景:
// 定义一个接口Product,它声明了所有具体产品对象必须实现的操作
type Product interface {
operation() // 产品对象的操作
}
// 定义具体产品ConcreteProductA,实现了Product接口
type ConcreteProductA struct{}
func (p *ConcreteProductA) operation() {
fmt.Println("Operation of ConcreteProductA")
}
// 定义另一个具体产品ConcreteProductB,也实现了Product接口
type ConcreteProductB struct{}
func (p *ConcreteProductB) operation() {
fmt.Println("Operation of ConcreteProductB")
}
// 定义一个抽象工厂Creator,它声明了工厂方法factoryMethod,用于创建产品对象
type Creator interface {
factoryMethod() Product // 工厂方法,用于创建产品对象
}
// 定义具体工厂CreatorA,实现了Creator接口
type CreatorA struct{}
func (c *CreatorA) factoryMethod() Product {
return &ConcreteProductA{} // 具体工厂CreatorA返回ConcreteProductA的实例
}
// 定义另一个具体工厂CreatorB,也实现了Creator接口
type CreatorB struct{}
func (c *CreatorB) factoryMethod() Product {
return &ConcreteProductB{} // 具体工厂CreatorB返回ConcreteProductB的实例
}
func main() {
// 创建具体工厂CreatorA的实例
creatorA := &CreatorA{}
productA := creatorA.factoryMethod()
productA.operation() // 调用产品A的操作
// 创建具体工厂CreatorB的实例
creatorB := &CreatorB{}
productB := creatorB.factoryMethod()
productB.operation() // 调用产品B的操作
}
观察者模式是一种行为设计模式,它定义了对象间的一种一对多的依赖关系,使得当一个对象改变状态时,所有依赖于它的对象都会得到通知并自动更新。这种模式非常适合于实现分布式事件处理系统。
特点:
优点:
缺点:
应用场景:
// 定义Observer接口,它声明了观察者需要实现的Update方法
type Observer interface {
Update(string) // 当主题状态改变时,此方法会被调用
}
// 定义Subject结构体,它包含一个观察者列表和方法来添加或通知观察者
type Subject struct {
observers []Observer // 存储观察者的列表
}
// Attach方法用于将一个观察者添加到观察者列表中
func (s *Subject) Attach(observer Observer) {
s.observers = append(s.observers, observer)
}
// Notify方法用于通知所有观察者主题状态的改变
func (s *Subject) Notify(message string) {
for _, observer := range s.observers {
observer.Update(message) // 调用每个观察者的Update方法
}
}
// 定义一个具体观察者ConcreteObserver,它实现了Observer接口
type ConcreteObserverA struct {
name string
}
// 实现Observer接口的Update方法
func (c *ConcreteObserverA) Update(message string) {
fmt.Printf("%s received message: %s\n", c.name, message)
}
func main() {
// 创建主题对象
subject := &Subject{}
// 创建具体观察者对象
observerA := &ConcreteObserverA{name: "Observer A"}
// 将观察者添加到主题的观察者列表中
subject.Attach(observerA)
// 当主题状态改变时,通知所有观察者
subject.Notify("State changed to State 1")
}
装饰者模式是一种结构型设计模式,允许用户在不修改对象自身的基础上,通过添加装饰者对象来动态地给对象添加额外的职责或功能。
特点:
优点:
缺点:
应用场景:
// 定义Component接口,它是所有组件和装饰者的基类
type Component interface {
operation() // 组件执行的操作
}
// 定义具体组件ConcreteComponent,实现了Component接口
type ConcreteComponent struct{}
func (c *ConcreteComponent) operation() {
fmt.Println("ConcreteComponent: performing basic operation")
}
// 定义Decorator抽象结构体,它包含一个Component接口类型的字段
type Decorator struct {
component Component // 用于组合Component接口
}
// 实现Decorator的operation方法,调用其Component的operation方法
func (d *Decorator) operation() {
if d.component != nil {
d.component.operation() // 调用被装饰者的operation
}
}
// 定义具体装饰者ConcreteDecoratorA,它嵌入了Decorator结构体
type ConcreteDecoratorA struct {
Decorator // 继承Decorator,实现装饰功能
}
// 为ConcreteDecoratorA实现operation方法,添加额外的职责
func (cda *ConcreteDecoratorA) operation() {
cda.Decorator.operation() // 首先调用被装饰者的operation
fmt.Println("ConcreteDecoratorA: added additional responsibilities")
}
func main() {
// 创建具体组件
component := &ConcreteComponent{}
// 创建装饰者并关联具体组件
decoratorA := &ConcreteDecoratorA{Decorator{component}}
// 执行装饰后的组件操作
decoratorA.operation()
}
策略模式是一种行为设计模式,它定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法独立于使用它的客户端。
特点:
优点:
缺点:
应用场景:
// 定义Strategy接口,它声明了所有具体策略必须实现的algorithm方法
type Strategy interface {
algorithm() // 策略的算法方法
}
// 定义具体策略ConcreteStrategyA,实现了Strategy接口
type ConcreteStrategyA struct{}
func (c *ConcreteStrategyA) algorithm() {
fmt.Println("Executing Algorithm A")
}
// 定义另一个具体策略ConcreteStrategyB,也实现了Strategy接口
type ConcreteStrategyB struct{}
func (c *ConcreteStrategyB) algorithm() {
fmt.Println("Executing Algorithm B")
}
// 定义Context结构体,它包含一个Strategy接口类型的字段
type Context struct {
strategy Strategy // 用于存储当前使用的策略
}
// 执行策略的方法,通过Context中的strategy字段调用algorithm方法
func (c *Context) executeStrategy() {
c.strategy.algorithm() // 执行当前策略的算法
}
func main() {
// 创建Context对象
context := &Context{}
// 创建具体策略对象
strategyA := &ConcreteStrategyA{}
strategyB := &ConcreteStrategyB{}
// 将Context的策略设置为策略A
context.strategy = strategyA
context.executeStrategy() // 输出: Executing Algorithm A
// 更换策略为策略B
context.strategy = strategyB
context.executeStrategy() // 输出: Executing Algorithm B
}
适配器模式是一种结构型设计模式,用于使原本不兼容的接口能够一起工作。它通常涉及到一个客户端使用一个期望的特定接口,而另一个类或组件提供了一个不同的接口。适配器模式通过创建一个中间层(适配器),将一个类的接口转换成客户端期望的另一个接口。
特点:
优点:
缺点:
应用场景:
// 定义Target接口,表示客户端使用的特定领域相关的接口
type Target interface {
request() // 客户端期望调用的方法
}
// 定义一个已经存在的类Adaptee,它有自己的接口
type Adaptee struct{}
func (a *Adaptee) specificRequest() {
fmt.Println("Adaptee performs a specific request")
}
// 定义Adapter结构体,它作为Target接口和Adaptee类之间的桥梁
type Adapter struct {
adaptee *Adaptee // 引用Adaptee对象
}
// Adapter实现了Target接口的request方法
// 该方法内部委托给Adaptee的specificRequest方法
func (a *Adapter) request() {
if a.adaptee != nil {
a.adaptee.specificRequest() // 委托调用Adaptee的方法
}
}
func main() {
// 创建Adaptee对象
adaptee := &Adaptee{}
// 创建Adapter对象,并注入Adaptee对象
adapter := &Adapter{adaptee: adaptee}
// 客户端使用Target接口,这里通过Adapter实现
var target Target = adapter
target.request() // 通过Adapter调用Adaptee的方法
}
代理模式是一种结构型设计模式,它为另一个对象提供一个代替或占位符,以控制对它的访问。代理可以在不改变对象的代码前提下,通过引入代理对象来间接访问原始对象,从而在不直接暴露原始对象的情况下,提供额外的功能操作。
特点:
优点:
缺点:
应用场景:
// 定义一个Subject接口,它声明了真实主题和代理主题共有的接口。
type Subject interface {
request() // 声明一个请求方法,真实主题和代理主题都会实现这个方法。
}
// RealSubject 结构体实现了 Subject 接口,代表真实主题。
type RealSubject struct{}
// RealSubject 的 request 方法实现了 Subject 接口的 request 方法,用于执行实际的操作。
func (r *RealSubject) request() {
fmt.Println("Real Subject") // 打印 "Real Subject" 表示真实主题正在被调用。
}
// Proxy 结构体包含一个指向 RealSubject 的指针,它作为代理主题。
type Proxy struct {
realSubject *RealSubject // 代理主题包含一个对真实主题的引用,初始为 nil。
}
// Proxy 的 request 方法实现了 Subject 接口的 request 方法。
// 这个方法首先检查 realSubject 是否为 nil,如果是,则创建 RealSubject 的实例。
// 然后,调用 realSubject 的 request 方法,从而间接地实现了 Subject 接口的 request 方法。
func (p *Proxy) request() {
if p.realSubject == nil { // 如果 realSubject 为 nil,说明还没有创建真实主题的实例。
p.realSubject = &RealSubject{} // 创建 RealSubject 的实例,并赋值给 realSubject。
}
p.realSubject.request() // 调用真实主题的 request 方法。
}
命令模式是一种行为设计模式,它将一个请求或操作封装为一个对象。这种模式可以解耦请求的发送者和接收者,让它们不直接交互,而是通过命令对象来间接进行通信。
特点:
优点:
缺点:
应用场景:
// 定义Command接口,它声明了所有具体命令必须实现的Execute方法
type Command interface {
Execute() // 执行命令的方法
}
// 定义Receiver结构体,它将执行命令的实际请求
type Receiver struct{}
func (r *Receiver) Action() {
fmt.Println("Receiver: Action")
}
// 定义ConcreteCommand结构体,它实现了Command接口
// 每个具体命令都包含一个Receiver的引用,表示请求的接收者
type ConcreteCommand struct {
receiver *Receiver // 命令执行的接收者
}
// ConcreteCommand实现Command接口的Execute方法
// 该方法调用Receiver的Action方法来执行请求
func (c *ConcreteCommand) Execute() {
c.receiver.Action() // 执行请求
}
// 定义Invoker结构体,它负责调用命令对象的Execute方法
type Invoker struct {
command Command // 存储命令对象
}
// 调用命令对象的Execute方法
func (i *Invoker) Invoke() {
i.command.Execute() // 执行命令
}
func main() {
// 创建接收者对象
receiver := &Receiver{}
// 创建具体命令对象,并注入接收者
command := &ConcreteCommand{receiver: receiver}
// 创建调用者对象,并注入具体命令对象
invoker := &Invoker{command: command}
// 调用者执行命令
invoker.Invoke() // 输出: Receiver: Action
}
组合模式是一种结构型设计模式,它允许你将对象组合成树状结构,以表示“部分-整体”的层次结构。这种模式使得用户可以一致地对待单个对象和对象组合。
特点:
优点:
缺点:
应用场景:
// 定义Component接口,作为组合中对象的一致性协议
type Component interface {
Operation() // 执行操作的方法
Add(Component) // 向组合中添加子节点的方法
Remove(Component) // 从组合中移除子节点的方法
GetChild(int) Component // 根据索引获取子节点的方法
}
// 定义Leaf结构体,表示组合中的叶节点
type Leaf struct {
name string
}
// Leaf实现Component接口的Operation方法
func (l *Leaf) Operation() {
fmt.Println("Leaf:", l.name)
}
// Leaf实现Component接口的Add方法,叶节点不能有子节点,因此这里可以不实现或抛出错误
func (l *Leaf) Add(c Component) {
fmt.Println("Cannot add to a leaf")
}
// Leaf实现Component接口的Remove方法,叶节点不能有子节点,因此这里可以不实现或抛出错误
func (l *Leaf) Remove(c Component) {
fmt.Println("Cannot remove from a leaf")
}
// Leaf实现Component接口的GetChild方法,叶节点没有子节点,因此这里返回nil
func (l *Leaf) GetChild(i int) Component {
return nil
}
// 定义Composite结构体,表示组合中的容器节点
type Composite struct {
name string
Children []Component // 存储子节点的列表
}
// Composite实现Component接口的Operation方法
func (c *Composite) Operation() {
fmt.Println("Composite:", c.name)
for _, child := range c.Children {
child.Operation() // 递归调用子节点的Operation方法
}
}
// Composite实现Component接口的Add方法,向Children列表中添加子节点
func (c *Composite) Add(component Component) {
c.Children = append(c.Children, component)
}
// Composite实现Component接口的Remove方法,从Children列表中移除子节点
func (c *Composite) Remove(component Component) {
for i, child := range c.Children {
if child == component {
c.Children = append(c.Children[:i], c.Children[i+1:]...)
break
}
}
}
// Composite实现Component接口的GetChild方法,根据索引获取子节点
func (c *Composite) GetChild(i int) Component {
if i < 0 || i >= len(c.Children) {
return nil // 索引超出范围,返回nil
}
return c.Children[i]
}
func main() {
// 创建叶节点
leafA := &Leaf{name: "Leaf A"}
leafB := &Leaf{name: "Leaf B"}
// 创建组合节点
composite := &Composite{name: "Composite Root"}
composite.Add(leafA) // 向组合中添加叶节点A
composite.Add(leafB) // 向组合中添加叶节点B
// 执行组合节点的操作
composite.Operation()
}
迭代器模式是一种行为设计模式,它允许你顺序访问一个聚合对象中的各个元素而不需要暴露其内部的表示。迭代器模式提供了一种通过抽象迭代器来遍历元素的方法,使得你可以在不知道具体集合类型的情况下,对集合进行遍历。
特点:
优点:
缺点:
应用场景:
// 定义Iterator接口,它声明了迭代器必须实现的Next和Current方法
type Iterator interface {
Next() bool // 移动到下一个元素,并返回是否成功移动
Current() interface{} // 返回当前元素
}
// 定义ConcreteIterator结构体,它实现了Iterator接口
type ConcreteIterator struct {
items []string // 存储聚合对象的元素列表
index int // 当前迭代到的元素索引
}
// Next方法实现,用于移动到下一个元素
func (c *ConcreteIterator) Next() bool {
if c.index < len(c.items) {
c.index++ // 索引递增
return true
}
return false // 如果索引超出范围,返回false
}
// Current方法实现,用于返回当前元素
func (c *ConcreteIterator) Current() interface{} {
if c.index > 0 && c.index <= len(c.items) {
return c.items[c.index-1] // 返回当前索引的元素
}
return nil // 如果索引不在范围内,返回nil
}
// 定义Aggregate接口,表示聚合对象,它将负责创建迭代器
type Aggregate interface {
CreateIterator() Iterator // 创建并返回迭代器
}
// 定义ConcreteAggregate结构体,它实现了Aggregate接口
type ConcreteAggregate struct {
items []string // 聚合对象存储的元素列表
}
// CreateIterator方法实现,用于创建并返回一个迭代器
func (a *ConcreteAggregate) CreateIterator() Iterator {
return &ConcreteIterator{items: a.items, index: 0} // 返回一个新的迭代器实例
}
func main() {
// 创建聚合对象并添加元素
aggregate := &ConcreteAggregate{items: []string{"Item1", "Item2", "Item3"}}
// 使用聚合对象创建迭代器
iterator := aggregate.CreateIterator()
// 使用迭代器遍历聚合对象中的所有元素
for iterator.Next() {
fmt.Println(iterator.Current())
}
}
-End-
原创作者|王顺驰