模板方法模式是什麼 模版方法模式是設計模式中的行為型的一種模式,它在基類中定義了一個演算法的框架,允許子類在不修改結構的情況下重寫演算法的特定步驟。 為什麼要用模板方法模式 模板方法將整個演算法轉換為一系列獨立的步驟,以便子類能對其進行擴展,同時還可讓超類中所定義的結構保持完整。或者當多個類的演算法步驟一致 ...
模板方法模式是什麼
模版方法模式是設計模式中的行為型的一種模式,它在基類中定義了一個演算法的框架,允許子類在不修改結構的情況下重寫演算法的特定步驟。
為什麼要用模板方法模式
模板方法將整個演算法轉換為一系列獨立的步驟,以便子類能對其進行擴展,同時還可讓超類中所定義的結構保持完整。或者當多個類的演算法步驟一致,只是一些細微之處不同時,可用該模式。
模板方法模式怎麼實現
讓我們來考慮一個一次性密碼功能(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)
}
優點:
- 它在父類中提取了公共的部分代碼,便於代碼復用。
- 它封裝了不變部分,擴展可變部分。它把認為是不變部分的演算法封裝到父類中實現,而把可變部分演算法由子類繼承實現,便於子類繼續擴展。
- 部分方法是由子類實現的,因此子類可以通過擴展方式增加相應的功能,符合開閉原則。
缺點:
- 對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象,間接地增加了系統實現和維護的複雜度。