Нужна клёвая книга о паттернах на русском? Вот она »
Шаблонный метод

Шаблонный метод на Go

Шаблонный метод — это поведенческий паттерн, задающий скелет алгоритма в суперклассе и заставляющий подклассы реализовать конкретные шаги этого алгоритма.

Концептуальный пример

Давайте разберем пример функционала одноразового пароля (OTP – One Time Password). Он может быть доставлен пользователю разными путями (СМС, электронная почта и т. д.), но независимо от способа доставки, сам процесс OTP один и тот же:

  1. Создать случайное число с n-ым количеством цифр.
  2. Сохранить этот номер в кэш для дальнейшей верификации.
  3. Подготовить содержимое.
  4. Отправить оповещение.
  5. Опубликовать метрику.

Возможные OTP, которые будут представлены в будущем, скорее всего также будут использовать процедуру выше.

В таком случае шаги конкретной операции одинаковы, но их реализация может отличаться. Такая ситуация подходит для использования паттерна Шаблонный метод.

Сперва мы определим базовый шаблонный алгоритм, который состоит из фиксированного количества методов. Это и будет нашим шаблонным методом. После этого мы реализуем методы для каждого шага, но шаблонный метод при этом трогать не будем.

otp.go: Шаблонный метод

package main

type iOtp interface {
	genRandomOTP(int) string
	saveOTPCache(string)
	getMessage(string) string
	sendNotification(string) error
	publishMetric()
}

// 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
// 	}
// 	iOtp.publishMetric()
// 	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
	}
	o.iOtp.publishMetric()
	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
}

func (s *sms) publishMetric() {
	fmt.Printf("SMS: publishing metrics\n")
}

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
}

func (s *email) publishMetric() {
	fmt.Printf("EMAIL: publishing metrics\n")
}

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
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
По материалам: Golang By Example

Шаблонный метод на других языках программирования

Шаблонный метод на Java Шаблонный метод на C# Шаблонный метод на C++ Шаблонный метод на PHP Шаблонный метод на Python Шаблонный метод на Ruby Шаблонный метод на Swift Шаблонный метод на TypeScript