十大经典设计模式及其Go语言实现
前言
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
1. 单例模式(Singleton Pattern)
特点:确保一个类只有一个实例,并提供一个全局访问点。
优点:保证资源或状态的全局唯一性,减少资源消耗。
缺点:反模块化,测试困难。
应用场景:配置管理器、连接池、日志记录器等。
代码示例:
type singleton struct {
value string
}
var instance *singleton
// 定义一个结构体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)
// 再次获取单例实例,将返回相同的实例
anotherInstance := getInstance()
if singletonInstance == anotherInstance {
fmt.Println("Both instances are the same")
}
}
2. 工厂模式(Factory Pattern)
特点:封装对象创建过程,由子类决定实例化哪个类。
优点:提高模块独立性,易于扩展。
缺点:类数量可能急剧增加,工厂类可能过于庞大。
应用场景:数据库连接、GUI组件、支付网关等。
代码示例:
// 定义产品接口,声明了所有具体产品对象必须实现的操作
type Product interface {
operation()
}
// 定义具体产品实现接口
type ConcreteProductA struct{}
func (p *ConcreteProductA) operation() {
fmt.Println("Operation of ConcreteProductA")
}
// 定义抽象工厂接口,声明了工厂方法
type Creator interface {
factoryMethod() Product
}
// 定义具体工厂实现接口
type CreatorA struct{}
func (c *CreatorA) factoryMethod() Product {
return &ConcreteProductA{} // 返回具体产品的实例
}
func main() {
// 使用工厂创建产品
creatorA := &CreatorA{}
productA := creatorA.factoryMethod()
productA.operation()
}
3. 观察者模式(Observer Pattern)
特点:定义对象间一对多的依赖关系,自动更新依赖对象。
优点:降低耦合度,扩展性好。
缺点:可能造成性能问题,系统难以维护。
应用场景:事件监听系统、UI更新、消息系统等。
代码示例:
// 定义观察者接口,声明了更新方法
type Observer interface {
Update(string)
}
// 定义主题,包含观察者列表和注册、通知观察者的方法
type Subject struct {
observers []Observer
}
func (s *Subject) Attach(observer Observer) {
s.observers = append(s.observers, observer)
}
func (s *Subject) Notify(message string) {
for _, observer := range s.observers {
observer.Update(message) // 通知所有观察者
}
}
// 定义具体观察者实现接口
type ConcreteObserverA struct {
name string
}
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") // 通知观察者
}
4. 装饰者模式(Decorator Pattern)
特点:动态地给对象添加额外职责或功能。
优点:增加职责动态且可撤销,多个装饰者可组合使用。
缺点:系统复杂,可能影响性能。
应用场景:日志记录、缓存、安全控制等。
代码示例:
// 定义组件接口,声明了组件的操作
type Component interface {
operation()
}
// 定义具体组件实现接口
type ConcreteComponent struct{}
func (c *ConcreteComponent) operation() {
fmt.Println("ConcreteComponent: performing basic operation")
}
// 定义装饰者抽象类,包含组件接口类型的字段
type Decorator struct {
component Component
}
// 装饰者实现组件的操作,委托给组件
func (d *Decorator) operation() {
if d.component != nil {
d.component.operation()
}
}
// 定义具体装饰者实现装饰者抽象类
type ConcreteDecoratorA struct {
Decorator
}
func (cda *ConcreteDecoratorA) operation() {
cda.Decorator.operation() // 调用装饰的组件操作
fmt.Println("ConcreteDecoratorA: added additional responsibilities")
}
func main() {
component := &ConcreteComponent{}
decoratorA := &ConcreteDecoratorA{Decorator{component}}
decoratorA.operation() // 装饰后的操作
}
5. 策略模式(Strategy Pattern)
特点:定义算法家族,封装起来,互相替换。
优点:算法变化独立于客户端,易于添加新算法。
缺点:客户端需要了解策略类差异。
应用场景:算法选择、支付方式、排序算法等。
代码示例:
// 定义策略接口,声明了策略的算法方法
type Strategy interface {
algorithm()
}
// 定义具体策略实现接口
type ConcreteStrategyA struct{}
func (c *ConcreteStrategyA) algorithm() {
fmt.Println("Executing Algorithm A")
}
// 定义上下文环境,包含策略接口类型的字段
type Context struct {
strategy Strategy
}
// 上下文环境执行策略的方法
func (c *Context) executeStrategy() {
c.strategy.algorithm()
}
func main() {
context := &Context{}
strategyA := &ConcreteStrategyA{}
context.strategy = strategyA // 设置策略
context.executeStrategy() // 执行策略
}
6. 适配器模式(Adapter Pattern)
特点:使不兼容接口能一起工作。
优点:增加兼容性,客户端代码无需修改。
缺点:系统复杂,可能引入性能开销。
应用场景:系统集成、第三方库集成等。
代码示例:
// 定义客户端期望的接口
type Target interface {
request()
}
// 定义一个已经存在的类,有自己的接口
type Adaptee struct{}
func (a *Adaptee) specificRequest() {
fmt.Println("Adaptee performs a specific request")
}
// 定义适配器,作为Target接口和Adaptee类之间的桥梁
type Adapter struct {
adaptee *Adaptee
}
func (a *Adapter) request() {
if a.adaptee != nil {
a.adaptee.specificRequest() // 委托调用Adaptee的方法
}
}
func main() {
adaptee := &Adaptee{}
adapter := &Adapter{adaptee: adaptee}
var target Target = adapter // 客户端通过适配器使用Adaptee
target.request()
}
7. 代理模式(Proxy Pattern)
特点:为另一个对象提供代替或占位符,控制访问。
优点:降低耦合度,增加可控性,代码可扩展。
缺点:增加系统复杂性,可能引入性能开销。
应用场景:访问控制、延迟初始化、远程代理等。
代码示例:
// 定义主题接口,声明了请求方法
type Subject interface {
request()
}
// 定义真实主题,实现了主题接口
type RealSubject struct{}
func (r *RealSubject) request() {
fmt.Println("Real Subject")
}
// 定义代理主题,包含对真实主题的引用
type Proxy struct {
realSubject *RealSubject
}
func (p *Proxy) request() {
if p.realSubject == nil {
p.realSubject = &RealSubject{} // 如果没有真实主题,则创建一个
}
p.realSubject.request() // 调用真实主题的请求方法
}
8. 命令模式(Command Pattern)
特点:将请求或操作封装为对象,解耦发送者和接收者。
优点:降低耦合度,增加操作灵活性,易于扩展。
缺点:可能产生大量命令类。
应用场景:事务处理、撤销操作、日志请求等。
代码示例:
// 定义命令接口,声明了执行方法
type Command interface {
Execute()
}
// 定义接收者,将执行命令的实际请求
type Receiver struct{}
func (r *Receiver) Action() {
fmt.Println("Receiver: Action")
}
// 定义具体命令,实现命令接口,包含接收者的引用
type ConcreteCommand struct {
receiver *Receiver
}
func (c *ConcreteCommand) Execute() {
c.receiver.Action() // 执行接收者的操作
}
// 定义调用者,负责调用命令对象的执行方法
type Invoker struct {
command Command
}
func (i *Invoker) Invoke() {
i.command.Execute() // 执行命令
}
func main() {
receiver := &Receiver{}
command := &ConcreteCommand{receiver: receiver} // 创建具体命令对象,并注入接收者
invoker := &Invoker{command: command} // 创建调用者对象,并注入具体命令对象
invoker.Invoke() // 调用者执行命令
}
9. 组合模式(Composite Pattern)
特点:将对象组合成树状结构,一致对待单个对象和组合。
优点:简化客户端代码,更好的层次结构表示。
缺点:设计复杂,需要合理设计接口和类。
应用场景:文件系统、组织结构、GUI组件等。
代码示例:
// 定义组件接口,作为组合中对象的一致性协议
type Component interface {
Operation()
Add(Component)
Remove(Component)
GetChild(int) Component
}
// 定义叶节点,实现组件接口
type Leaf struct {
name string
}
func (l *Leaf) Operation() {
fmt.Println("Leaf:", l.name)
}
func (l *Leaf) Add(c Component) {
fmt.Println("Cannot add to a leaf")
}
func (l *Leaf) Remove(c Component) {
fmt.Println("Cannot remove from a leaf")
}
func (l *Leaf) GetChild(i int) Component {
return nil
}
// 定义组合节点,实现组件接口
type Composite struct {
name string
Children []Component
}
func (c *Composite) Operation() {
fmt.Println("Composite:", c.name)
for _, child := range c.Children {
child.Operation()
}
}
func (c *Composite) Add(component Component) {
c.Children = append(c.Children, component)
}
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
}
}
}
func (c *Composite) GetChild(i int) Component {
if i < 0 || i >= len(c.Children) {
return 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() //
}
10. 迭代器模式(Iterator Pattern)
特点:顺序访问聚合对象元素,不暴露内部表示。
优点:抽象化集合访问,支持多种遍历方式,增加灵活性。
缺点:增加系统复杂性,需要额外代码实现迭代器。
应用场景:遍历集合、数据结构、数据库查询等。
代码示例:
// 定义迭代器接口,声明了遍历集合的方法
type Iterator interface {
Next() bool // 移动到下一个元素
Current() interface{} // 返回当前元素
}
// 定义具体迭代器实现接口
type ConcreteIterator struct {
items []string // 存储聚合对象的元素列表
index int // 当前迭代到的元素索引
}
func (c *ConcreteIterator) Next() bool {
if c.index < len(c.items) {
c.index++
return true
}
return false
}
func (c *ConcreteIterator) Current() interface{} {
if c.index > 0 && c.index <= len(c.items) {
return c.items[c.index-1]
}
return nil
}
// 定义聚合对象接口,声明了创建迭代器的方法
type Aggregate interface {
CreateIterator() Iterator // 创建并返回迭代器
}
// 定义具体聚合对象实现接口
type ConcreteAggregate struct {
items []string // 聚合对象存储的元素列表
}
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())
}
}
这些设计模式是软件工程中常用的解决方案,可以帮助开发者提高代码的可重用性、可读性和可靠性。