设计模式之模板模式

顾名思义,定义一个模板,使用模板的子类可以往模板里面填写不同内容,但是模板的结构不能改变。

同样的场景,有发送短信,发送邮件,发送后都需要记录日志,发送前还需要进行内容校验,防止有一些不法信息。

用模板模式,就需要提取出发短信和发邮件两个操作的共同点,有两个,第一个是内容拦截,第二个是记录日志,

而不同点是发送的执行,这需要让他们自己去做。

在这里插入图片描述

定义出一个如上图所示的UML图,SenderService是一个抽象类,其中有模板方法。

抽象方法send()是让子类自己实现,log()方法可以实现也不可实现。

interceptor()方法是校验非法内容的,不容其他子类修改。

这个模板类最终代码如下:

/**
 * @author lw
 * @date 2022/3/30 0030
 * @description 消息模板类
 */
public abstract  class SenderService {

    /**
     * 提供一个钩子函数,让子类可以自己实现自己的日志
     * 否则就使用默认日志记录方式
     */
    public void log(){
        System.out.println("SenderService.log");
    }

    /**
     * 信息拦截
     * @param content
     * @return
     */
    private String interceptor(String content){
        if("不法信息".equals(content)){
            content = "*****";
        }
        return content;
    }

    /**
     * 消息发送  由具体执行的类来决定
     * @param args
     */
    protected abstract void send(String ...args);

    /**
     * 模板方法  模板方法不能被子类改写,所以需要定义为final
     * @param args
     */
    public final void sendMsg(String ...args){
        String content = this.interceptor(args[1]);
        args[1] = content;
        this.send(args);
        this.log();
    }

}

最主要的是sendMsg()这个模板方法,里面定义了操作顺序,外界就通过这个方法来发送消息。

调用者

String toPhone = "12306";
String content = "hello world";

SenderService smsService = new SmsService();
smsService.sendMsg(toPhone,content);

SenderService mailService = new MailService();
mailService.sendMsg(toPhone,"不法信息");

//输出如下
短信发送:hello world
SmsService.log
    
邮件发送:*****
SenderService.log

在这里插入图片描述

定义一个接口,有多个实现类,这是模板模式么?

其实一般不算的,模板模式里面有个模板方法,这里面有方法调用顺序和结构,接口里面一般没有的。

除非这个接口里用了java8的新特性,如下代码所示,接口里面还有默认的方法。

/**
 * @author lw
 * @date 2022/3/30 0030
 * @description
 */
public interface SenderService {

    void send(String ...args);

    //模板方法
    default void sendMsg(String ...args){
        this.intercept();
        this.send(args);
        this.log();
    }

    default void log(){
        System.out.println("SenderService.log");
    }

    default void intercept(){
        System.out.println("SenderService.intercept");
    }
}

这个勉强算是用了模板模式,sendMsg()也勉强算是模板方法。

为何说勉强,sendMsg()是可以被子类实现,并改变顺序的。

public class SmsService implements SenderService{
    @Override
    public void send(String ...args) {
        System.out.println("短信发送:"+args[0]);
    }

    @Override
    public void sendMsg(String... args) {
        this.send(args);
    }
}

SenderService senderService = new SmsService();
senderService.sendMsg("hello world");

//实现了sendMsg后的   输出
短信发送:hello world
    
//没有实现sendMsg后的输出
SenderService.intercept
短信发送:hello world
SenderService.log

子类改变了模板方法的结构和执行逻辑。

个人感悟

说说模板模式的使用,模板模式因为定义了执行逻辑,这就像一个流程一样,如果流程是多变,不稳定的,那么应该考虑怎么使用模板模型。

比如,定义了拦截内容–>发送短信—>记录日志。突然改为了记录日志–>拦截内容–>记录日志–>发送短信—>记录日志。

如果是单独写在SmsService和MailService内的,那么就需要改两个类。

而如果流程改变是不同的,比如SmsService中是 记录日志–>拦截内容–>记录日志–>发送短信—>记录日志。

而MailService中是 拦截内容–>发送短信—>记录日志。

假如记录日志是log()方法,那么MailService中如下实现为空,则后续日志也记录不了,那么就需要拆分两个日志记录方式。

public void log(){
	
}


//拆分日志
public void logBeforeSend(){
	
}
public void logAfterSend(){
	
}

这种对于流程不统一的,设置流程单独变化的,要么使用可以修改模板方法的方式(不加final),要么还是单独拆开吧。

这是当初血一样的教训,好不容易改成了模板模式,结果其中一个类的流程改变了,就要单独拆分出来。

总结

模板模式需要定义模板类和模板方法,后续子类实现模板中对应的内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值