
Шаблонный метод на Go
Шаблонный метод — это поведенческий паттерн, задающий скелет алгоритма в суперклассе и заставляющий подклассы реализовать конкретные шаги этого алгоритма.
Концептуальный пример
Давайте разберем пример функционала одноразового пароля (OTP – One Time Password). Он может быть доставлен пользователю разными путями (СМС, электронная почта и т. д.), но независимо от способа доставки, сам процесс OTP один и тот же:
- Создать случайное число с n-ым количеством цифр.
- Сохранить этот номер в кэш для дальнейшей верификации.
- Подготовить содержимое.
- Отправить оповещение.
Возможные OTP, которые будут представлены в будущем, скорее всего также будут использовать процедуру выше.
В таком случае шаги конкретной операции одинаковы, но их реализация может отличаться. Такая ситуация подходит для использования паттерна Шаблонный метод.
Сперва мы определим базовый шаблонный алгоритм, который состоит из фиксированного количества методов. Это и будет нашим шаблонным методом. После этого мы реализуем методы для каждого шага, но шаблонный метод при этом трогать не будем.
otp.go: Шаблонный метод
package main
type IOtp interface {
genRandomOTP(int) string
saveOTPCache(string)
getMessage(string) string
sendNotification(string) error
}
// type otp struct {
// }
// func (o *otp) genAndSendOTP(iOtp iOtp, otpLength int) error {
// otp := iOtp.genRandomOTP(otpLength)
// iOtp.saveOTPCache(otp)
// message := iOtp.getMessage(otp)
// err := iOtp.sendNotification(message)
// if err != nil {
// return err
// }
// return nil
// }
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
}
return nil
}
sms.go: Конкретная реализация
package main
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
}
email.go: Конкретная реализация
package main
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
}
main.go: Клиентский код
package main
import "fmt"
func main() {
// otp := otp{}
// smsOTP := &sms{
// otp: otp,
// }
// smsOTP.genAndSendOTP(smsOTP, 4)
// emailOTP := &email{
// otp: otp,
// }
// emailOTP.genAndSendOTP(emailOTP, 4)
// fmt.Scanln()
smsOTP := &Sms{}
o := Otp{
iOtp: smsOTP,
}
o.genAndSendOTP(4)
fmt.Println("")
emailOTP := &Email{}
o = Otp{
iOtp: emailOTP,
}
o.genAndSendOTP(4)
}
output.txt: Результат выполнения
SMS: generating random otp 1234
SMS: saving otp: 1234 to cache
SMS: sending sms: SMS OTP for login is 1234
EMAIL: generating random otp 1234
EMAIL: saving otp: 1234 to cache
EMAIL: sending email: EMAIL OTP for login is 1234