设计模式系列:模板方法模式
一、问题引入
在面向对象程序设计中,经常会遇到这种情况:知道了一个过程的关键步骤,且确定了这些步骤的执行顺序,但某些步骤的具体实现未知,或者说某些步骤的实现与具体的环境相关。这种情况下就可以使用模板模式。
例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对工作人员评分等,其中取号、排队和对银行工作人员评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以另外在子类中实现。
这样的例子在生活中还有很多,例如,一个人每天会起床、吃饭、做事、睡觉等,其中“做事”的内容每天可能不同。我们把这些规定了流程或格式的实例定义成模板。
二、模式定义
以下介绍的模板方法模式将解决以上类似的问题。
1、模板模式在书中定义:
定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
通俗点的理解就是:完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。
2.、模板模式的类图:
抽象父类(AbstractClass):实现了模板方法,定义了算法的骨架。
具体类(ConcreteClass):实现抽象类中的抽象方法,即不同的对象的具体实现细节。
3、模式实例:
去银行办业务步骤:(1)取号 (2)办具体业务 (3)服务评价打分。这三个步骤就可以抽取到父类中进行定义,(1)取号和(3)服务打分是相同操作,可以直接在父类中实现,然后(2)办具体的业务各不相同需要在子类中实现。
//银行业务抽象父类 abstract class BankBusiness { public void TemplateMethod() //模板方法 { takeNum(); doBusiness(); rate(); } public void takeNum() //取号 { log.info("用户取号"); } public void rate() //打分 { log.info("用户打分"); } public abstract void doBusiness(); //办业务 } //开户业务子类 class Account extends BankBusiness { public void doBusiness() { log.info("用户开户"); } } //转账业务子类 class Transfer extends BankBusiness { public void doBusiness() { log.info("用户转账"); } } //测试调用类 public class TemplateMethodPattern { public static void main(String[] args) { BankBusiness bb = new Account(); bb.TemplateMethod(); } }
三、项目应用
模板方法模式在实际项目中的应用。物联网网关收到设备端发送的数据请求后,会根据不同的请求类型进行不同的业务处理。业务处理步骤都是(1)读取解析请求数据、(2)处理具体的请求、(3)响应请求、(4)连接心跳检测。其中,(1)、(3)、(4)是所有请求的通用步骤,而(2)则需要根据不同的请求进行不同的处理。实例代码如下:
//业务抽象父类 @slfj public abstract class AbstractMqttHandler extends SimpleChannelInboundHandler<MqttMessage>{ @Autowired private INetworkAlertService iNetworkAlertService; //建立连接,解析数据 @Override protected void channelRead0(ChannelHandlerContext ctx, MqttMessage msg) throws Exception { log.info("连接建立,名称【{}】",ctx.name()); MqttFixedHeader mqttFixedHeader = msg.fixedHeader(); //调用doMessage方法,根据不同的请求进行不同的处理 Optional.ofNullable(mqttFixedHeader) .ifPresent( t -> doMessage(ctx, msg)); } //处理请求 protected abstract void doMessage(ChannelHandlerContext ctx, MqttMessage mqttMessage); //连接关闭 @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); String deviceSn = ChannelUtil.getDeviceSn(channel); if(StringUtils.isBlank(deviceSn)) return; MqttChannel mqttChannel = MqttServerHandlerDispather.mqttChannels.get(deviceSn); log.info("MqttChannler = 【{}】, 登录状态【{}】", JsonUtil.toJson(mqttChannel), mqttChannel.isLogin()); if(mqttChannel != null && mqttChannel.isCleanSession()) { MqttServerHandlerDispather.mqttChannels.remove(deviceSn); log.info("====关闭连接,设备号为:==="+deviceSn); } } } //业务实现子类 public class MqttServerHandlerDispather extends AbstractMqttHandler { @Override protected void doMessage(ChannelHandlerContext ctx, MqttMessage mqttMessage){ Channel channel = ctx.channel(); MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader(); //如果是连接请求 if(mqttFixedHeader.messageType().equals(MqttMessageType.CONNECT)) { if(protocolProcess.getConnect().processConnect(channel,mqttMessage)) { ...... } } MqttChannel mqttChannel = mqttChannels.get(ChannelUtil.getDeviceSn(channel)); //判断channel和登录状态 if(mqttChannel != null && mqttChannel.isLogin()){ //如果是处理业务 switch (mqttFixedHeader.messageType()) { case PUBLISH: log.info("==================case PUBLISH======进入处理mq消息流程=============="); protocolProcess.getPublish().processPublish(channel,mqttMessage); break; case PINGREQ: log.info("==================case PINGREQ===回复确认========"); protocolProcess.getPingReq().processPingReq(channel,mqttMessage); break; case DISCONNECT: log.info("==================case DISCONNECT======解除连接============="); protocolProcess.getDisConnect().processDisConnect(channel,mqttMessage); break; ...... } } } }
四、总结
模板方法模式适合那些有固定的一系列流程,但其中某些步骤需要根据不同业务进行不同处理的业务问题。