元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
设计原则
单一职责原则
开闭原则
里氏替换原则
依赖倒置原则
接口隔离原则
迪米特法则
▶
创建型模式
工厂方法模式
抽象工厂
单例模式
建造者模式
原型模式
▶
结构型模式
适配器模式
装饰器模式
代理模式
外观模式
组合模式
桥接模式
享元模式
▶
行为型模式
策略模式
观察者模式
命令模式
模板方法模式
状态模式
责任链模式
迭代器模式
中介者模式
访问者模式
备忘录模式
解释器模式
发布时间:
2025-03-21 15:22
↑
☰
# 模板方法模式 ## 概述 模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 ## 问题场景 在软件开发中,我们经常会遇到以下场景: 1. 多个类有相似的算法步骤 2. 算法的整体步骤是固定的,但某些步骤的实现可能不同 3. 需要控制子类扩展的部分 ## 解决方案 模板方法模式通过以下方式解决这些问题: ```mermaid classDiagram class AbstractClass { <<abstract>> +templateMethod() #step1() #step2() #step3() #hook() } class ConcreteClassA { #step1() #step2() #step3() #hook() } class ConcreteClassB { #step1() #step2() #step3() #hook() } AbstractClass <|-- ConcreteClassA AbstractClass <|-- ConcreteClassB ``` 主要角色: 1. 抽象类(AbstractClass): 定义抽象的原语操作,实现一个模板方法作为算法的骨架 2. 具体类(ConcreteClass): 实现原语操作以完成算法中与特定子类相关的步骤 ## 代码示例 ### 1. 基本实现 ```go // AbstractClass 抽象类 type AbstractClass interface { TemplateMethod() string step1() string step2() string step3() string hook() bool } // Template 模板基类 type Template struct { abstract AbstractClass } // NewTemplate 创建模板 func NewTemplate(abstract AbstractClass) *Template { return &Template{abstract: abstract} } // TemplateMethod 模板方法 func (t *Template) TemplateMethod() string { result := t.abstract.step1() if t.abstract.hook() { result += "\n" + t.abstract.step2() } result += "\n" + t.abstract.step3() return result } // ConcreteClassA 具体类A type ConcreteClassA struct { *Template } func NewConcreteClassA() *ConcreteClassA { concrete := &ConcreteClassA{} concrete.Template = NewTemplate(concrete) return concrete } func (c *ConcreteClassA) step1() string { return "ConcreteClassA: Step 1" } func (c *ConcreteClassA) step2() string { return "ConcreteClassA: Step 2" } func (c *ConcreteClassA) step3() string { return "ConcreteClassA: Step 3" } func (c *ConcreteClassA) hook() bool { return true } // ConcreteClassB 具体类B type ConcreteClassB struct { *Template } func NewConcreteClassB() *ConcreteClassB { concrete := &ConcreteClassB{} concrete.Template = NewTemplate(concrete) return concrete } func (c *ConcreteClassB) step1() string { return "ConcreteClassB: Step 1" } func (c *ConcreteClassB) step2() string { return "ConcreteClassB: Step 2" } func (c *ConcreteClassB) step3() string { return "ConcreteClassB: Step 3" } func (c *ConcreteClassB) hook() bool { return false } ``` ### 2. 实际应用示例 ```go // 以数据导出为例 // DataExporter 数据导出接口 type DataExporter interface { Export() string connect() string fetchData() string formatData() string shouldCompress() bool compress() string } // BaseExporter 基础导出器 type BaseExporter struct { exporter DataExporter } func NewBaseExporter(exporter DataExporter) *BaseExporter { return &BaseExporter{exporter: exporter} } func (b *BaseExporter) Export() string { result := b.exporter.connect() result += "\n" + b.exporter.fetchData() result += "\n" + b.exporter.formatData() if b.exporter.shouldCompress() { result += "\n" + b.exporter.compress() } return result } // PDFExporter PDF导出器 type PDFExporter struct { *BaseExporter } func NewPDFExporter() *PDFExporter { exporter := &PDFExporter{} exporter.BaseExporter = NewBaseExporter(exporter) return exporter } func (p *PDFExporter) connect() string { return "Connecting to PDF database" } func (p *PDFExporter) fetchData() string { return "Fetching PDF data" } func (p *PDFExporter) formatData() string { return "Formatting data to PDF" } func (p *PDFExporter) shouldCompress() bool { return true } func (p *PDFExporter) compress() string { return "Compressing PDF file" } // ExcelExporter Excel导出器 type ExcelExporter struct { *BaseExporter } func NewExcelExporter() *ExcelExporter { exporter := &ExcelExporter{} exporter.BaseExporter = NewBaseExporter(exporter) return exporter } func (e *ExcelExporter) connect() string { return "Connecting to Excel database" } func (e *ExcelExporter) fetchData() string { return "Fetching Excel data" } func (e *ExcelExporter) formatData() string { return "Formatting data to Excel" } func (e *ExcelExporter) shouldCompress() bool { return false } func (e *ExcelExporter) compress() string { return "Compressing Excel file" } // 使用示例 func main() { // 创建PDF导出器 pdfExporter := NewPDFExporter() fmt.Println("PDF Export Process:") fmt.Println(pdfExporter.Export()) fmt.Println("\nExcel Export Process:") // 创建Excel导出器 excelExporter := NewExcelExporter() fmt.Println(excelExporter.Export()) } ``` ## 适用场景 1. 算法的固定步骤 - 一次性实现算法的不变部分 - 各个步骤有固定的执行顺序 2. 代码复用 - 多个类有相似的行为 - 需要消除代码重复 3. 扩展限制 - 需要控制子类扩展 - 只允许在特定点进行扩展 ## 优缺点 ### 优点 1. 代码复用 - 将公共代码放在父类 - 避免代码重复 2. 扩展灵活 - 子类可以重写算法步骤 - 保持算法结构不变 3. 反向控制 - 父类调用子类方法 - 实现反向控制结构 ### 缺点 1. 维护困难 - 每个步骤的子类实现都要维护 - 子类执行顺序约束 2. 代码限制 - 算法骨架固定 - 子类受限于父类 ## 实现要点 1. 抽象方法设计 - 识别公共步骤 - 定义抽象方法 2. 钩子方法 - 提供默认实现 - 子类可选择性重写 3. 步骤封装 - 封装不变部分 - 开放可变部分 ## 相关模式 1. 工厂方法模式 - 常被模板方法调用 - 创建所需对象 2. 策略模式 - 改变整个算法 - 更灵活但更复杂 3. 建造者模式 - 构建复杂对象 - 步骤更加灵活 ## 总结 模板方法模式是一种基于继承的代码复用技术,它的核心思想是: 1. 算法骨架 - 在父类定义算法骨架 - 确保算法结构稳定 2. 步骤实现 - 子类实现具体步骤 - 保持算法框架不变 3. 扩展方式 - 提供钩子方法 - 控制扩展方式 在实际开发中,模板方法模式常用于框架设计中,它可以提供一个框架性的解决方案,同时还允许子类进行特定的扩展。使用时需要注意父类和子类之间的依赖关系,以及算法步骤的划分是否合理。