Golang设计模式之-模版模式

举报
kaliarch 发表于 2022/01/28 09:29:18 2022/01/28
【摘要】 golang设计模式之模版模式 一 模版模式简介模版方法模式使用继承机制,把通用步骤和通用方法放到父类中,把具体实现延迟到子类中实现。使得实现符合开闭原则。如实例代码中通用步骤在父类中实现(准备、下载、保存、收尾)下载和保存的具体实现留到子类中,并且提供 保存方法的默认实现。因为Golang不提供继承机制,需要使用匿名组合模拟实现继承。此处需要注意:因为父类需要调用子类方法,所以子类需要匿...

golang设计模式之模版模式

一 模版模式简介

模版方法模式使用继承机制,把通用步骤和通用方法放到父类中,把具体实现延迟到子类中实现。使得实现符合开闭原则。

如实例代码中通用步骤在父类中实现(准备下载保存收尾)下载和保存的具体实现留到子类中,并且提供 保存方法的默认实现。

因为Golang不提供继承机制,需要使用匿名组合模拟实现继承。

此处需要注意:因为父类需要调用子类方法,所以子类需要匿名组合父类的同时,父类需要持有子类的引用。

在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。

例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。

这样的例子在生活中还有很多,例如,一个人每天会起床、吃饭、做事、睡觉等,其中“做事”的内容每天可能不同。我们把这些规定了流程或格式的实例定义成模板,允许使用者根据自己的需求去更新它,例如,简历模板、论文模板、Word 中模板文件等。

以下介绍的模板方法模式将解决以上类似的问题。

二 代码

2.1 开发代码

package templatemethod

import "fmt"

type iOtp interface {
	genRandomOTP(int) string
	saveOTPCache(string)
	getMessage(string) string
	sendNotification(string) error
	publishMetric()
}

// 模版模式基于继承,在类层面进行
type Otp struct {
	iOtp iOtp
}

func (i Otp) genAndSendMsg(optLength int) error {
	opt := i.iOtp.genRandomOTP(optLength)
	i.iOtp.saveOTPCache(opt)
	msg := i.iOtp.getMessage(opt)
	if err := i.iOtp.sendNotification(msg); err != nil {
		return err
	}
	i.iOtp.publishMetric()
	return nil
}

// 模版模式基于继承,在类层面进行
type Email struct {
	Otp
}

// 继承重写父类方法
func (e *Email) genRandomOTP(int) string {
	fmt.Println("email gen random otp")
	return fmt.Sprintf("%s, gen random otp", e)
}

func (e *Email) saveOTPCache(otp string) {
	fmt.Println("email save otp", otp)
}

func (e *Email) getMessage(opt string) string {
	return fmt.Sprintf("%s,msg", opt)
}
func (e *Email) sendNotification(msg string) error {
	fmt.Println("e send notification", msg)
	return nil
}
func (e *Email) publishMetric() {
	fmt.Println("email publish")
}

type SMS struct {
	Otp
}

// 继承重写父类方法
func (e *SMS) genRandomOTP(int) string {
	fmt.Println("SMS gen random otp")
	return fmt.Sprintf("%s, gen random otp", e)
}

func (e *SMS) saveOTPCache(otp string) {
	fmt.Println("SMS save otp", otp)
}

func (e *SMS) getMessage(opt string) string {
	return fmt.Sprintf("%s,msg", opt)
}
func (e *SMS) sendNotification(msg string) error {
	fmt.Println("SMS send notification", msg)
	return nil
}
func (e *SMS) publishMetric() {
	fmt.Println("SMS publish")
}

2.2 测试

package templatemethod

import (
	"fmt"
	"testing"
)

const (
	emailLength = 10
	SmsLength   = 20
)

func TestOtp(t *testing.T) {

	emopt := &Email{}

	opt := Otp{
		iOtp: emopt,
	}

	_ = opt.genAndSendMsg(emailLength)

	fmt.Println("")

	smsopt := &SMS{}

	opt = Otp{
		iOtp: smsopt,
	}
	_ = opt.genAndSendMsg(SmsLength)
}

2.3 运行结果

email gen random otp
email save otp &{{<nil>}}, gen random otp
e send notification &{{<nil>}}, gen random otp,msg
email publish

SMS gen random otp
SMS save otp &{{<nil>}}, gen random otp
SMS send notification &{{<nil>}}, gen random otp,msg
SMS publish
PASS

三 应用场景

四 特点

4.1 优点

  • 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共的部分代码,便于代码复用。
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

4.2 缺点

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
  • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

参考链接

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。