模板方法模式
简介
模板方法模式(Template Method Pattern)可以在基类中定义一个算法的 框架,允许子类在不修改框架结构的情况下重写算法的特定步骤。
角色组成:
- 抽象类(Abstract Class):声明作为算法步骤的方法,以及依次调用它们的实际模板方法。
- 具体类(Concret Class):可以重写所有步骤,但不能重写模板方法自身。
使用场景
- 框架扩展点设计
- 多步骤流程标准化
- 算法流程复用
- 生命周期控制
- 代码复用与规范约束
优点
- 提高代码复用性(公共流程在父类实现)
- 约束子类行为(强制实现关键步骤)
- 符合开闭原则(扩展子类而非修改父类)
- 提供清晰的扩展点(钩子方法)
缺点
- 子类数量可能过多(每个差异需一个子类)
- 父类定义流程,子类灵活性受限
- 继承强耦合(子类依赖父类实现细节)
- 复杂流程可能导致模板方法难以维护
示例代码
Simple
实现一次性密码功能,将一次性密码发送给用户的 方式有多种,如短信、邮件,但无论哪种方式,流程都是相同的:
- 生成随机n位数字
- 在缓存中存储这组数字,以便验证
- 准备工作
- 发送通知
- 发布
- Go
- Python
package templatemethod
import (
"fmt"
"math"
"math/rand"
)
type IOtp interface {
GenRandomOTP(int) int
SaveOTPCache(int)
GetMessage(int) string
SendNotification(string) error
Publish()
}
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.Publish()
return nil
}
type Sms struct {
Otp
}
func (s *Sms) GenRandomOTP(length int) int {
// rand.Seed(time.Now().UnixNano())
min := int(math.Pow10(length - 1))
max := int(math.Pow10(length)) - 1
return rand.Intn(max-min+1) + min
}
func (s *Sms) SaveOTPCache(otp int) {
fmt.Printf("Saving otp %d to cache\n", otp)
}
func (s *Sms) GetMessage(otp int) string {
return fmt.Sprintf("Your OTP is %d", otp)
}
func (s *Sms) SendNotification(message string) error {
fmt.Printf("Sending sms: %s\n", message)
return nil
}
func (s *Sms) Publish() {
fmt.Println("Publishing message as SMS")
}
客户端
package templatemethod
import "testing"
func TestTemplateMethod(t *testing.T) {
sms := &Sms{}
o := Otp{sms}
err := o.GenAndSendOTP(6)
if err != nil {
t.Errorf("Error sending OTP: %v\n", err)
}
}
from abc import ABCMeta, abstractmethod
import random
class IOtp(metaclass=ABCMeta):
@abstractmethod
def gen_random_otp(self) -> int:
pass
@abstractmethod
def save_otp_cache(self):
pass
@abstractmethod
def get_message(self):
pass
@abstractmethod
def send_notification(self):
pass
@abstractmethod
def publish(self):
pass
class Otp:
def __init__(self):
self.iotp: IOtp = None
def gen_and_send_otp(self, otp_length: int):
otp = self.iotp.gen_random_otp(otp_length)
self.iotp.save_otp_cache(otp)
message = self.iotp.get_message()
try:
self.iotp.send_notification(message)
except Exception as e:
raise e
else:
self.iotp.publish()
class Sms(IOtp):
def __init__(self):
self._otp = None
def gen_random_otp(self, length: int = 6) -> int:
return random.randint(10 ** (length - 1), (10**length) - 1)
def save_otp_cache(self, otp: int):
self._otp = otp
print(f"save otp {otp} into cache")
def get_message(self) -> str:
return f"Your OTP is {self._otp}"
def send_notification(self, message: str):
print(f"send notification via sms: {message}")
def publish(self):
print("publish sms")
if __name__ == "__main__":
otp = Otp()
otp.iotp = Sms()
otp.gen_and_send_otp(6)