【设计模式】13、template 模板模式

发布于:2024-05-05 ⋅ 阅读:(29) ⋅ 点赞:(0)


十三、template 模板模式

https://refactoringguru.cn/design-patterns/template-method

如果是一套标准流程, 但有多种实现, 可以用 template 模板模式. 可以让父类定义整套流程, 子类实现各具体步骤. golang 中可通过 组合 interface 实现继承, 详见例子.

例如, 如果要开发一个数据挖掘程序, 支持输入 word, csv, pdf. 他们其实是相同的流程(如打开文件, 读取数据, 转换数据, 输出数据).

因为每种类别的流程相同, 所以每种类别的实现有很多重复代码.

为了消除这些重复代码, 可以定义基类, 实现通用的逻辑, 如果有个性化的逻辑再覆盖.

源码详见:godp/13template at master · datager/godp · GitHub

13.1 ppl

https://refactoringguru.cn/design-patterns/template-method/go/example

go 虽然没有继承, 但可通过 interface 和 组合实现. 框架如下:

type iTemplate interface {  
    step1()    
    step2()
}  

// 外部使用的基类  
type wrapper struct {  
    template iTemplate
}  

// 基类的方法: 串联整个流程  
func (w *wrapper) run() {  
    w.template.step1()    
    w.template.step2()
}  

然后有如下具体实现:

type impl1 struct {}  
func (i *impl1) step1() {}  
func (i *impl1) step2() {}  

type impl2 struct {}  
func (i *impl2) step1() {}  
func (i *impl2) step2() {}  

使用:

// 第一种实现  
w := &wrapper{template: &impl1{}}  
w.run()  

// 第二种实现  
w := &wrapper{template: &impl2{}}  
w.run()  

13.1.1 目录层级

├── llm_ppl.go
├── ocr_ppl.go
├── ppl.go
├── ppl_test.go
└── readme.md

13.1.2 ppl_test.go

package _31ppl

import (
	"fmt"
	"testing"
)

/*
=== RUN   TestLLMPipeline
llmPipeline 开始检测
llmPipeline 开始分类
llmPipeline 无需分割

ocrPipeline 开始检测
ocrPipeline 开始分类
ocrPipeline 需要分割
ocrPipeline 开始分割
--- PASS: TestLLMPipeline (0.00s)
PASS
*/
func TestLLMPipeline(t *testing.T) {
	e := &aiEngine{ppl: &llmPipeline{}}
	e.runPipeline()

	fmt.Println()
	e = &aiEngine{ppl: &ocrPipeline{}}
	e.runPipeline()
}

13.1.3 ppl.go

package _31ppl

// imagePipeline 图像处理流程
type imagePipeline interface {
	// 检测
	detect()
	// 分类
	classification()
	// 是否需要分割, 可影响父类的流程. 将它留给子类实现, 使子类的具体实现可影响父类的流程
	needSegmentation() bool
	// 分割
	segmentation()
}

type aiEngine struct {
	ppl imagePipeline
}

// go 通过组合, 对 interface 封装了一层, 封装的是 interface 的整个流程
func (e *aiEngine) runPipeline() {
	e.ppl.detect()
	e.ppl.classification()
	if e.ppl.needSegmentation() { // 父类的流程可能会变化, 取决于具体子类的实现
		e.ppl.segmentation()
	}
}

13.1.4 llm_ppl.go

package _31ppl

import "fmt"

type llmPipeline struct{}

func (ppl *llmPipeline) detect() {
	fmt.Println("llmPipeline 开始检测")
}

func (ppl *llmPipeline) classification() {
	fmt.Println("llmPipeline 开始分类")
}

func (ppl *llmPipeline) needSegmentation() bool {
	fmt.Println("llmPipeline 无需分割")
	return false
}

func (ppl *llmPipeline) segmentation() {
	panic("llmPipeline 无需分割, 流程错误!!!")
}

13.1.5 ocr_ppl.go

package _31ppl

import "fmt"

type ocrPipeline struct{}

func (ppl *ocrPipeline) detect() {
	fmt.Println("ocrPipeline 开始检测")
}

func (ppl *ocrPipeline) classification() {
	fmt.Println("ocrPipeline 开始分类")
}

func (ppl *ocrPipeline) needSegmentation() bool {
	fmt.Println("ocrPipeline 需要分割")
	return true
}

func (ppl *ocrPipeline) segmentation() {
	fmt.Println("ocrPipeline 开始分割")
}

网站公告

今日签到

点亮在社区的每一天
去签到