行為型:二. 模板方法模式
阿新 • • 發佈:2022-04-12
模板方法模式是什麼
模版方法模式是設計模式中的行為型的一種模式,它在基類中定義了一個演算法的框架,允許子類在不修改結構的情況下重寫演算法的特定步驟。
為什麼要用模板方法模式
模板方法將整個演算法轉換為一系列獨立的步驟,以便子類能對其進行擴充套件,同時還可讓超類中所定義的結構保持完整。或者當多個類的演算法步驟一致,只是一些細微之處不同時,可用該模式。
模板方法模式怎麼實現
讓我們來考慮一個一次性密碼功能(OTP)的例子。將OTP傳遞給使用者的方式多種多樣(簡訊、郵件等)。但無論是簡訊還是郵件, 整個 OTP 流程都是相同的:
- 生成隨機的 n 位數字。
- 在快取中儲存這組數字以便進行後續驗證。
- 準備內容。
- 傳送通知。
- 釋出。
我們定義了固定的模板方法。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)
}
優點:
- 它在父類中提取了公共的部分程式碼,便於程式碼複用。
- 它封裝了不變部分,擴充套件可變部分。它把認為是不變部分的演算法封裝到父類中實現,而把可變部分演算法由子類繼承實現,便於子類繼續擴充套件。
- 部分方法是由子類實現的,因此子類可以通過擴充套件方式增加相應的功能,符合開閉原則。
缺點:
- 對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象,間接地增加了系統實現和維護的複雜度。