👉目录
1 单例模式(Singleton Pattern)
2 工厂模式(Factory Pattern)
3 观察者模式(Observer Pattern)
4 装饰者模式(Decorator Pattern)
5 策略模式(Strategy Pattern)
6 适配器模式(Adapter Pattern)
7 代理模式(Proxy Pattern)
8 命令模式(Command Pattern)
9 组合模式(Composite Pattern)
10 迭代器模式(Iterator Pattern)
软件设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它的意义在于这些模式是众多程序员经过相当长的一段时间的试验和错误复盘所总结的宝贵经验,可以帮助我们提高代码的可重用性、可读性和可靠性。本文由腾讯云天御业务安全工程师王顺驰撰写,总结了 10 种经典的软件设计模式的特点、优缺点和应用场景,并给出了相应代码示例。
关注腾讯云开发者,一手技术干货提前解锁👇
01
单例模式(Singleton Pattern)
单例模式是一种创建型设计模式,它限制了实例化类的对象个数,确保在任何情况下,一个类只有一个实例,并且提供一个全局访问点。这种模式在需要全局状态控制或共享资源访问时非常有用。
特点:
只有一个实例对象。
必须自行创建实例对象。
必须提供一个访问该实例的全局访问点。
优点:
确保在应用中,资源或状态的全局唯一性。
减少系统资源消耗,提高系统效率。
缺点:
反模块化,因为单例对象需要被多个客户端引用,这违反了高内聚低耦合的设计原则。
难以测试,因为单例对象的生命周期与应用相同,这使得在单元测试中进行隔离测试变得困难。
应用场景:
配置管理器:在应用程序中,配置信息通常只需要一个实例来管理,这样可以保证配置信息的一致性。
连接池:数据库连接池需要限制数据库连接的数量,以避免过多的连接消耗资源。
日志记录器:日志系统通常只需要一个实例来记录应用程序的日志信息,以避免日志信息的冗余和混乱。
硬件管理器:对于某些硬件设备,如打印机或扫描仪,可能只需要一个管理器来控制对它们的访问。
应用状态管理:在某些应用中,需要全局的状态管理,如用户会话管理或权限验证状态。
// 定义一个结构体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()
}
02
工厂模式(Factory Pattern)
工厂模式是一种创建型设计模式,用于将对象的创建过程封装起来,由子类决定实例化哪一个类。这种模式使得代码结构更加清晰,并且能够轻松替换或扩展产品类。
特点:
封装性:将对象的创建过程封装在工厂类中。
扩展性:通过继承和多态,可以轻松地添加新的产品类。
抽象性:工厂方法定义了创建对象的接口,但具体对象的创建由子类实现。
优点:
将对象的创建和使用分离,提高了模块间的独立性。
易于扩展,增加新的产品类时不需要修改现有代码,符合开闭原则。
缺点:
每增加一个产品类,就需要增加一个具体的工厂类,这可能会导致类的数量急剧增加。
工厂类集中了所有实例的创建逻辑,可能会导致工厂类过于庞大。
应用场景:
数据库连接:根据不同的数据库类型,如 MySQL、PostgreSQL,创建相应的数据库连接对象。
GUI 组件:在图形用户界面开发中,不同的操作系统可能需要不同的组件实现,工厂模式可以根据不同平台创建相应的组件。
支付网关:根据不同的支付方式,如信用卡、PayPal、微信支付,创建相应的支付处理对象。
图像处理:在图像处理软件中,根据不同的文件格式,如 JPEG、PNG,创建相应的图像处理器。
// 定义一个接口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的操作
}
03
观察者模式(Observer Pattern)
观察者模式是一种行为设计模式,它定义了对象间的一种一对多的依赖关系,使得当一个对象改变状态时,所有依赖于它的对象都会得到通知并自动更新。这种模式非常适合于实现分布式事件处理系统。
特点:
一对多关系:一个主题可以有多个观察者。
抽象耦合:观察者和主题之间是抽象耦合的,增加新的观察者不会影响现有的系统。
动态联动:观察者可以在任何时候加入或退出。
优点:
降低了对象之间的耦合度,主题与观察者之间是松散耦合的。
扩展性好,增加新的观察者或主题类不影响现有的类。
缺点:
当观察者对象很多时,通知的分发可能会造成性能问题。
如果观察者和主题之间的依赖关系过于复杂,会导致系统难以维护。
应用场景:
事件监听系统:在 GUI 应用程序中,用户界面组件(如按钮、文本框等)可以作为观察者,监听用户的输入事件。
UI 更新:在应用程序中,当数据模型发生变化时,界面需要相应地更新,使用观察者模式可以自动完成这一过程。
消息系统:在即时通讯软件中,当有新消息到达时,所有在线的用户(观察者)都会收到通知。
股票市场:股票价格更新时,所有订阅了该股票的投资者(观察者)都会收到最新价格信息。
资源监控:在系统监控工具中,当系统资源(如 CPU、内存使用率)超过设定阈值时,监控系统(观察者)会收到通知并采取相应措施。
// 定义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")
}
04
装饰者模式(Decorator Pattern)
装饰者模式是一种结构型设计模式,允许用户在不修改对象自身的基础上,通过添加装饰者对象来动态地给对象添加额外的职责或功能。
特点:
动态扩展:可以在运行时动态地给对象添加职责。
透明性:装饰者模式不改变对象的接口,因此对客户端来说是透明的。
灵活性:可以多个装饰者组合使用,为对象添加多个职责。
优点:
增加对象的职责是动态的、可撤销的。
可以用多个装饰者包装一个对象,添加多个职责。
装饰者和对象可以独立变化,不会相互耦合。
缺点:
过度使用装饰者模式可能会使系统变得复杂,难以理解。
可能会引起多层装饰者调用,影响性能。
应用场景:
日志记录:在不修改原有对象的基础上,添加日志记录功能。
缓存:为对象的某些方法添加缓存功能,以提高性能。
安全控制:为对象添加访问控制,如权限检查。
事务处理:为数据库操作添加事务管理功能。
性能监控:为对象的方法添加性能监控功能,以分析性能瓶颈。
资源管理:为资源使用添加额外的管理功能,如连接池的管理。
// 定义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()
}
05
策略模式(Strategy Pattern)
策略模式是一种行为设计模式,它定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法独立于使用它的客户端。
特点:
封装变化:将变化的部分封装起来,与稳定部分分离。
多态性:使用多态来实现算法的替换。
替换性:在运行时选择使用哪个策略。
优点:
算法的变化独立于使用算法的客户端。
容易添加新算法而不影响客户端。
缺点:
客户端必须了解所有策略类的差异,以便使用适当的策略。
应用场景:
算法选择:在应用程序中,根据不同的业务需求选择不同的算法。
支付方式:在电子商务平台,根据用户选择提供不同的支付方式。
排序算法:在数据处理中,根据不同的数据特性选择不同的排序算法。
路径查找:在地图服务中,根据不同的优化标准(如时间最短、距离最短)选择不同的路径查找算法。
游戏 AI:在游戏开发中,不同的敌人或角色使用不同的 AI 策略。
// 定义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
}
06
适配器模式(Adapter Pattern)
适配器模式是一种结构型设计模式,用于使原本不兼容的接口能够一起工作。它通常涉及到一个客户端使用一个期望的特定接口,而另一个类或组件提供了一个不同的接口。适配器模式通过创建一个中间层(适配器),将一个类的接口转换成客户端期望的另一个接口。
特点:
接口转换:适配器模式提供了将一个类的接口转换成另一种接口的方式。
兼容性:解决了接口不兼容的问题,使得原本不能一起工作的类可以协同工作。
优点:
增加了类的兼容性,使得它们可以一起工作,即使它们的接口不兼容。
客户端代码不需要修改,通过适配器与目标类交互。
缺点:
过度使用适配器模式可能会使系统变得复杂,难以理解和维护。
适配器可能会引入性能开销,尤其是在需要频繁调用适配器方法的情况下。
应用场景:
不同系统的集成:当需要将两个使用不同接口的系统集成时,可以使用适配器模式。
第三方库的集成:当使用一个第三方库,但其接口与现有系统不兼容时,可以通过适配器模式进行集成。
硬件设备控制:在硬件设备控制领域,不同的设备可能有不同的控制接口,适配器模式可以用来统一这些接口。
新旧系统迁移:在新旧系统迁移过程中,旧系统中的组件可能需要适配新系统的接口。
模块化设计:在模块化设计中,适配器模式可以用来连接不同模块,即使它们的接口不兼容。
// 定义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的方法
}
07
代理模式(Proxy Pattern)
代理模式是一种结构型设计模式,它为另一个对象提供一个代替或占位符,以控制对它的访问。代理可以在不改变对象的代码前提下,通过引入代理对象来间接访问原始对象,从而在不直接暴露原始对象的情况下,提供额外的功能操作。
特点:
间接访问:通过代理对象来间接访问原始对象。
职责分离:将控制逻辑与业务逻辑分离,代理对象负责控制逻辑,原始对象负责业务逻辑。
延迟初始化:代理可以在需要时才创建原始对象,实现延迟初始化。
优点:
降低了系统的耦合度,增强了对象的可控性。
可以为原始对象提供额外的安全控制或延迟加载等操作。
增加了代码的可扩展性,可以在不修改原始对象的情况下,通过引入新的代理类来扩展功能。
缺点:
增加了系统的复杂性,可能会让系统设计变得更加复杂。
可能会引入性能开销,尤其是在代理对象需要进行复杂控制逻辑时。
应用场景:
访问控制:在需要对对象访问进行权限控制时,可以使用代理模式。
延迟初始化:对于资源消耗较大的对象,可以使用代理模式实现延迟加载。
远程代理:为远程对象或网络资源提供代理,隐藏对象位于不同地址空间的事实。
虚拟代理:为复杂的对象创建一个简单的代理,以简化访问。
保护代理:控制对原始对象的访问,提供访问前后的附加操作。
智能引用:在访问对象时进行引用计数,当没有引用时自动释放资源。
// 定义一个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 方法。
}
08
命令模式(Command Pattern)
命令模式是一种行为设计模式,它将一个请求或操作封装为一个对象。这种模式可以解耦请求的发送者和接收者,让它们不直接交互,而是通过命令对象来间接进行通信。
特点:
封装性:命令模式将请求封装为一个对象,隐藏了请求的具体实现细节。
扩展性:可以方便地添加新的命令类,无需修改现有代码。
灵活性:命令对象可以被存储、传递、排队、记录和修改。
优点:
降低了系统耦合度,请求发送者和接收者之间通过命令对象交互。
增加了操作的灵活性,如支持撤销、重做、事务等操作。
易于扩展,可以独立地添加新的命令。
缺点:
可能会有大量的命令类,特别是命令的实现逻辑复杂时。
应用场景:
事务处理:在需要支持事务操作的系统中,命令模式可以封装事务请求,支持事务的提交和回滚。
撤销操作:在需要撤销功能的系统中,命令对象可以存储状态,以便在需要时撤销操作。
日志请求:在需要记录用户操作的系统中,命令对象可以记录操作日志,用于审计或恢复操作。
批处理系统:在批处理系统中,命令对象可以表示一个批处理任务,支持任务的调度和执行。
宏录制:在需要宏录制功能的系统中,命令对象可以封装一系列操作,形成宏命令。
// 定义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
}
09
组合模式(Composite Pattern)
组合模式是一种结构型设计模式,它允许你将对象组合成树状结构,以表示“部分-整体”的层次结构。这种模式使得用户可以一致地对待单个对象和对象组合。
特点:
部分-整体层次结构:可以包含其他组合或叶节点,形成树状结构。
一致性:客户端代码可以一致地处理组合结构和叶节点。
优点:
简化了客户端代码,客户端可以统一处理组合结构和对象。
更好的层次结构表示,易于扩展和维护。
缺点:
设计较复杂,需要合理地设计组件的接口和类。
应用场景:
文件系统:文件系统中的文件和文件夹可以形成树状结构,其中文件夹可以包含文件和其他文件夹。
组织结构:公司的组织结构可以表示为树状结构,其中每个部门可以包含员工和其他子部门。
GUI 组件:在图形用户界面开发中,组件可以包含其他组件,形成复杂的界面结构。
分布式系统:在分布式系统中,资源可以组合成树状结构,以方便管理和访问。
企业资源规划(ERP):ERP 系统中,产品可以由多个部件组成,部件又可以进一步分解为子部件。
// 定义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()
}
10
迭代器模式(Iterator Pattern)
迭代器模式是一种行为设计模式,它允许你顺序访问一个聚合对象中的各个元素而不需要暴露其内部的表示。迭代器模式提供了一种通过抽象迭代器来遍历元素的方法,使得你可以在不知道具体集合类型的情况下,对集合进行遍历。
特点:
抽象化遍历过程:迭代器定义了遍历元素的接口。
支持多种遍历方式:不同的迭代器可以实现不同的遍历策略。
聚合对象与迭代器解耦:聚合对象不需要知道迭代器的具体实现。
优点:
抽象化集合的访问,使客户端代码与集合的内部表示无关。
可以提供多种遍历方式,如正序或逆序遍历。
增加了集合的灵活性,可以在不修改集合类的情况下,引入新的遍历方式。
缺点:
增加了系统的复杂性,需要为每个聚合类设计迭代器类。
需要额外的代码来实现迭代器。
应用场景:
遍历集合:在需要遍历集合元素的系统中,迭代器模式提供了一种通用的遍历机制。
数据结构:在实现复杂的数据结构如树、图等时,迭代器模式可以用来遍历结构中的节点。
数据库查询:在数据库查询中,迭代器可以用来逐条访问查询结果。
用户界面:在用户界面开发中,迭代器可以用来遍历界面元素。
多维数组访问:在需要访问多维数组元素的系统中,迭代器可以提供一种顺序访问的方式。
// 定义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-
原创作者|王顺驰
感谢你读到这里,不如关注一下?👇
你还使用过哪些软件设计模式?它们都有什么优点?欢迎评论分享。我们将选取点赞本文并且留言评论的一位读者,送出腾讯云开发者定制发财按键1个(见下图)。9月6日中午12点开奖。
📢📢欢迎加入腾讯云开发者社群,享前沿资讯、大咖干货,找兴趣搭子,交同城好友,更有鹅厂招聘机会、限量周边好礼等你来~
(长按图片立即扫码)