
Шаблонний метод на Go
Шаблонний метод — це поведінковий патерн, який визначає кістяк алгоритму в суперкласі та змушує підкласи реалізувати конкретні кроки цього алгоритму.
Концептуальний приклад
Давайте розглянемо приклад функціоналу одноразового пароля (OTP — One Time Password). Він може бути доставлений користувачеві різними шляхами (SMS, електронна пошта і т.ін.), але незалежно від способу доставки, сам процес 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