REBAJA de primavera
Template Method

Template Method en Go

Template Method es un patrón de diseño de comportamiento que te permite definir el esqueleto de un algoritmo en una clase base y permite a las subclases sobrescribir los pasos sin cambiar la estructura general del algoritmo.

Ejemplo conceptual

Consideremos el ejemplo de la funcionalidad de la contraseña de un solo uso (OTP, por sus siglas en inglés). Hay varias formas de entregarle la OTP a un usuario (SMS, correo electrónico, etc.). Pero, independientemente de si se trata de un SMS o un correo electrónico, el proceso OTP es el mismo:

  1. Generar un número de n dígitos al azar.
  2. Guardar el número en caché para su posterior verificación.
  3. Preparar el contenido.
  4. Enviar la notificación.

Todos los nuevos tipos de OTP que se introduzcan en el futuro, probablemente pasen por todos estos pasos.

De modo que tenemos un escenario en el que los pasos de una operación concreta son los mismos, pero la implementación de estos pasos puede variar. Ésta es una situación adecuada para considerar utilizar el patrón Template Method.

Primero, definimos un algoritmo plantilla base que consista en un número fijo de métodos. Éste será nuestro método plantilla. Después, implementaremos cada uno de los métodos de los pasos, pero dejaremos el método plantilla sin variar.

otp.go: Método plantilla

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: Implementación concreta

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: Implementación concreta

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: Código cliente

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: Resultado de la ejecución

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

Template Method en otros lenguajes

Template Method en C# Template Method en C++ Template Method en Java Template Method en PHP Template Method en Python Template Method en Ruby Template Method en Rust Template Method en Swift Template Method en TypeScript