GoLang design pattern 16 - template method pattern

Posted by Marino on Sun, 21 Nov 2021 20:15:09 +0100

Template method design pattern is a behavioral design pattern. This pattern is commonly used to define a template or algorithm model for a specific operation.

Take OTP (One Time Password) as an example. There are two common one-time passwords: SMS password (SMS OTP) or email password (Email OTP). However, the processing steps for both SMS password and email password are the same, as follows:

  1. Generate a random string
  2. Save the string into the cache for subsequent validation
  3. Prepare notification content
  4. Send notification
  5. Record statistics

In the above steps, except that the specific method of "sending notice" in Item 4 is different, other steps remain unchanged. Even if there is a new one-time password sending method in the future, it can be predicted that the above steps will remain unchanged.

In such scenarios, that is, the steps of an operation are fixed, but there are differences in the specific execution methods. At this time, we can use the template method mode. In the template method mode, we usually define a template interface or algorithm model interface for this operation. The interface contains fixed methods, and then the specific implementation class rewrites the relevant interfaces and implements these operations.

The following is the implementation of the one-time password example:

otp.go

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

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
}

Briefly interpret this Code:

  • The iOtp interface is defined in the above code. The methods in this interface are the relevant steps required by OTP
  • The following sms and email are the implementation of iOtp interface
  • The template method genAndSendOTP() is defined in the structure otp

In addition, note that in the above code, the iOtp interface and the structure otp are combined to provide an implementation similar to an abstract class. This approach can be used for reference when you need it.

sms.go

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

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

import "fmt"

func main() {
	smsOTP := &sms{}
	o := otp{
		iOtp: smsOTP,
	}
	o.genAndSendOTP(4)
	fmt.Println("")
	emailOTP := &email{}
	o = otp{
		iOtp: emailOTP,
	}
	o.genAndSendOTP(4)
}

The output content is:

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

Code uploaded to GitHub:   zhyea / go-patterns / template-method-pattern

END!!

Topics: Go Design Pattern