This article was done using my notes from:
Alexander Shvets (2019), Dive into Design Patterns, Refactoring.Guru
Template Method
Template Method is a behavioral design pattern that allows you to defines a skeleton of an algorithm in a base class and let subclasses override the steps without changing the overall algorithm’s structure.
Structure
Code
package main
import "fmt"
type IOtp interface {
genRandomOTP(int) string
saveOTPCache(string)
getMessage(string) string
sendNotification(string) error
}
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
}
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
}
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 main() {
smsOTP := &Sms{}
o := Otp{
iOtp: smsOTP,
}
o.genAndSendOTP(4)
fmt.Println("")
emailOTP := &Email{}
o = Otp{
iOtp: emailOTP,
}
o.genAndSendOTP(4)
}