1. 程式人生 > 其它 >行為型:二. 模板方法模式

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

模板方法模式是什麼

模版方法模式是設計模式中的行為型的一種模式,它在基類中定義了一個演算法的框架,允許子類在不修改結構的情況下重寫演算法的特定步驟。

為什麼要用模板方法模式

模板方法將整個演算法轉換為一系列獨立的步驟,以便子類能對其進行擴充套件,同時還可讓超類中所定義的結構保持完整。或者當多個類的演算法步驟一致,只是一些細微之處不同時,可用該模式。

模板方法模式怎麼實現

讓我們來考慮一個一次性密碼功能(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. 對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象,間接地增加了系統實現和維護的複雜度。