模闆方法設計模式是一種行為型設計模式。這種模式通過常用于為某種特定的操作定義一個模闆或者算法模型。
以一次性密碼(OTP:One Time Password)為例。我們常見的一次性密碼有兩種:短信密碼(SMS OTP)或者郵件密碼(Email OTP)。不過不管是短信密碼還是郵件密碼,它們的處理步驟都是一樣的,步驟如下:
- 生成一串随機字元串
- 将字元串儲存進緩存用來執行後續的驗證
- 準備通知内容
- 發送通知
- 記錄統計資訊
在以上的步驟中,除了第4項“發送通知”的具體方式不一樣,其他步驟都是不變的。即使以後有了新的一次性密碼發送方式,可以預見以上的步驟也是不變的。
在這類場景中,即一個操作的步驟是固定的,隻是在具體的執行方式上存在差異,這時我們就可以用到模闆方法模式了。在模闆方法模式中,我們通常會為這個操作定義一個模闆接口或算法模型接口,接口中包含固定方法,然後由具體的實作類來重寫相關接口并實作這些操作。
下面是一次性密碼這個例子的實作:
otp.go
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
}
簡單解讀下這段代碼:
- 在上面的代碼中定義了
接口,這個接口中的方法即OTP所需的相關步驟iOtp
- 下面的
和sms
是email
接口的實作iOtp
- 在結構體
中定義了模闆方法otp
genAndSendOTP()
此外注意下:在上面這段代碼中将
iOtp
接口和結構體
otp
合在了一起,提供了類似于抽象類的實作,這種做法大家需要的時候可以借鑒下。
sms.go
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")
}
email.go
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")
}
main.go
import "fmt"
func main() {
smsOTP := &sms{}
o := otp{
iOtp: smsOTP,
}
o.genAndSendOTP(4)
fmt.Println("")
emailOTP := &email{}
o = otp{
iOtp: emailOTP,
}
o.genAndSendOTP(4)
}
輸出内容為:
SMS: generating random otp 1234
SMS: saving otp: 1234 to cache
SMS: sending sms: SMS OTP for login is 1234
SMS: publishing metrics
EMAIL: generating random otp 1234
EMAIL: saving otp: 1234 to cache
EMAIL: sending email: EMAIL OTP for login is 1234
EMAIL: publishing metrics
代碼已上傳至GitHub: zhyea / go-patterns / template-method-pattern
END!!
僅是學習筆記,難免出錯,望不吝指點