 
                Шаблонний метод на 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