行为型:二. 模板方法模式

模板方法模式是什么

模版方法模式是设计模式中的行为型的一种模式,它在基类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。

为什么要用模板方法模式

模板方法将整个算法转换为一系列独立的步骤,以便子类能对其进行扩展,同时还可让超类中所定义的结构保持完整。或者当多个类的算法步骤一致,只是一些细微之处不同时,可用该模式。

模板方法模式怎么实现

让我们来考虑一个一次性密码功能(OTP)的例子。将OTP传递给用户的方式多种多样(短信、邮件等)。但无论是短信还是邮件, 整个 OTP 流程都是相同的:

  1. 生成随机的 n 位数字。
  2. 在缓存中保存这组数字以便进行后续验证。
  3. 准备内容。
  4. 发送通知。
  5. 发布。

我们定义了固定的模板方法。email和sms虽然实现了方式不同,但是都实现了相同的方法。后续如果有新的OTP类型也会实现相同的方法。

otp.go

package template_method


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

type otp struct {
    iOtp iOtp
}

func (o *otp) genAndSendOTP(otpLength int) error {
    otp := o.iOtp.genRandomOTP(otpLength)
    o.iOtp.saveOTPCache(otp)
    message := o.iOtp.getMessage(otp)
    err := o.iOtp.sendNotification(message)
    if err != nil {
        return err
    }
    o.iOtp.publishMetric()
    return nil
}

email.go

package template_method

import "fmt"

type email struct {
    otp
}

func (s *email) genRandomOTP(len int) string {
    randomOTP := "1234"
    fmt.Printf("EMAIL: generating random otp %s\n", randomOTP)
    return randomOTP
}

func (s *email) saveOTPCache(otp string) {
    fmt.Printf("EMAIL: saving otp: %s to cache\n", otp)
}

func (s *email) getMessage(otp string) string {
    return "EMAIL OTP for login is " + otp
}

func (s *email) sendNotification(message string) error {
    fmt.Printf("EMAIL: sending email: %s\n", message)
    return nil
}

func (s *email) publishMetric() {
    fmt.Printf("EMAIL: publishing metrics\n")
}

sms.go

package template_method

import "fmt"

type sms struct {
    otp
}

func (s *sms) genRandomOTP(len int) string {
    randomOTP := "1234"
    fmt.Printf("SMS: generating random otp %s\n", randomOTP)
    return randomOTP
}

func (s *sms) saveOTPCache(otp string) {
    fmt.Printf("SMS: saving otp: %s to cache\n", otp)
}

func (s *sms) getMessage(otp string) string {
    return "SMS OTP for login is " + otp
}

func (s *sms) sendNotification(message string) error {
    fmt.Printf("SMS: sending sms: %s\n", message)
    return nil
}

func (s *sms) publishMetric() {
    fmt.Printf("SMS: publishing metrics\n")
}

example.go客户端调用

package template_method

import "fmt"

func Example() {
    smsOTP := &sms{}
    o := otp{
        iOtp: smsOTP,
    }
    o.genAndSendOTP(4)

    fmt.Println("")
    emailOTP := &email{}
    o = otp{
        iOtp: emailOTP,
    }
    o.genAndSendOTP(4)
}

优点:

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

缺点:

  1. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现和维护的复杂度。
posted @ 2022-04-12 22:27  EthanWell  阅读(114)  评论(0编辑  收藏  举报