目录
如何学习本文
- 先对"概念(理论)“有大概印象,再将大量的"案例(实践)“看懂,如果看不懂可以抄写,多抄几遍就懂了。代码案例弄懂了之后倒回去看概念,你会发现概念描述的很清晰很准确。然后自己给自己提出一个需求,用本文的知识实现这个需求。自己写一篇关于"抽象类和模板方法设计模式"的博客或者笔记,最好是"脑图+文字+画图+代码”,笔记上传到云端,代码上传到github,将github作为自己的"代码片段仓库”,以后工作中需要这块知识,直接去自己的github上下载代码来使用即可。经常翻看关于这块知识的笔记与代码。
1. 概念
-
明确一点:接口的作用是提供能力、提供规范,抽象类定义模板。
-
模板方法设计模式 概述1:
- 模板方法是一个抽象类,里面的模板方法先把通用部分写好,然后声明一个钩子方法,让子类去实现,new的子类对象调用模板方法时,就会自动调用钩子方法了。
-
模板方法设计模式 概述2:
- 模板方法定义了算法(一段代码逻辑)的程序框架(先执行啥、后执行啥、怎么执行)。子类可以覆盖一个或多个算法步骤,以允许不同的行为,同时确保仍然遵循总体算法。
- 通常,此模式由两个或多个类组成,一个是提供模板方法(非抽象)的抽象类,该模板方法具有对由一个或多个具体子类实现的抽象方法的调用。
- 模板抽象类和具体实现通常位于同一项目中,但是根据项目范围,这些具体对象将被实现到另一个项目中。
-
模板方法设计模式 概述3:
- 定义一个操作中的算法框架,而将具体的实现细节推迟到子类中实现。这样可以使子类不改变算法结构来重新定义算法的某些特定逻辑。
-
模板方法设计模式角色:
- 抽象类:实现模板方法,定义算法框架。
- 具体类:实现抽象类中的抽象方法,完成完整的算法逻辑。
-
钩子方法:这种方法像一个钩子,抽象类下面挂哪个子类对象,就调用那个子类实现的钩子方法。
2. 案例
2.1 实现计算代码执行时间的抽象
-
涉及类
- cn.king.demo01.AbstractAClass 抽象类
- cn.king.demo01.AClass 抽象类的实现
- cn.king.demo01.Demo 作为测试类
-
cn.king.demo01.AbstractAClass
public abstract class AbstractAClass { public Integer process() { long start = System.currentTimeMillis(); Integer result = this.doProcess(); long end = System.currentTimeMillis(); System.out.println("花费时间为 " + (end - start) + " 毫秒"); return result; } public abstract Integer doProcess(); }
-
cn.king.demo01.AClass
public class AClass extends AbstractAClass{ @Override public Integer doProcess() { int result = 0; for (int i = 0; i < 100000; i++) { result += i; } return result; } }
-
cn.king.demo01.Demo
public class Demo { // 实际开发会使用这种方式 @Test public void test01() throws Exception { AClass aClass = new AClass(); System.out.println(aClass.process()); /* * 输出: * 花费时间为 2 毫秒 * 704982704 */ } @Test public void test02() throws Exception { AClass aClass = new AClass(); System.out.println(aClass.doProcess()); /* * 输出: * 704982704 */ } @Test public void test03() throws Exception { AbstractAClass aClass = new AClass(); System.out.println(aClass.process()); /* * 输出: * 花费时间为 1 毫秒 * 704982704 */ } @Test public void test04() throws Exception { AbstractAClass aClass = new AClass(); System.out.println(aClass.doProcess()); /* * 输出: * 704982704 */ } }
2.2 实现before()和after()的抽象
-
涉及类
- cn.king.demo01.AbstractAClass 抽象类
- cn.king.demo01.AClass 抽象类的实现
- cn.king.demo01.Demo 作为测试类
-
cn.king.demo01.AbstractAClass
public abstract class AbstractAClass { public void process() { before(); doProcess(); after(); } // 直接在抽象父类中实现 前置处理 protected void before() { System.out.println("执行AbstractAClass的before()方法"); } // 子类中实现 public abstract void doProcess(); // 直接在抽象父类中实现 后置处理 protected void after() { System.out.println("执行AbstractAClass的after()方法"); } }
-
cn.king.demo01.AClass
public class AClass extends AbstractAClass { // 如果觉得父类实现的before()或after()方法不满足自己需求,可以在子类中覆盖 @Override protected void before() { System.out.println("执行AClass的before()方法"); } @Override public void doProcess() { System.out.println("执行AClass的doProcess()方法"); } }
-
cn.king.demo01.Demo
public class Demo { @Test public void test01() throws Exception { AClass aClass = new AClass(); aClass.process(); /* * 输出: * 执行AClass的before()方法 * 执行AClass的doProcess()方法 * 执行AbstractAClass的after()方法 */ } }
2.3 实现异步处理的抽象
-
涉及类
- cn.king.demo01.AbstractAClass 抽象类
- cn.king.demo01.AClass 抽象类的实现
- cn.king.demo01.Demo 作为测试类
-
cn.king.demo01.AbstractAClass
public abstract class AbstractAClass<T> { // 异步处理 public void asyncProcess(T request) { new Thread(new Runnable() { @Override public void run() { T t = before(request); doAsyncProcess(t); } }).start(); } // 前置方法,可以对参数进行一些前置处理 protected abstract T before(T request); // 需要异步处理的代码 protected abstract void doAsyncProcess(T request); }
-
cn.king.demo01.AClass
public class AClass extends AbstractAClass<Integer> { @Override protected Integer before(Integer request) { if (request == null || request <= 0) { return 100000; } return request; } @Override protected void doAsyncProcess(Integer request) { int result = 0; for (int i = 0; i < request; i++) { result += i; } System.out.println("结果为 " + result); } }
-
cn.king.demo01.Demo
public class Demo { public static void main(String[] args) { AClass aClass = new AClass(); aClass.asyncProcess(null); // 输出:结果为 704982704 } }
2.4 案例4
-
需求:①打开链接 => ②处理数据 => ③关闭链接
-
分析:在抽象类中定义一个模板方法规定逻辑处理步骤为①②③。经常变动的是步骤②,将其抽取为抽象方法,不同的处理方式创建不同的实现类实现该方法。
-
抽象类
public abstract class URLProcessorBase { public void process(URL url) throws IOException { URLConnection urlConnection = url.openConnection(); InputStream input = urlConnection.getInputStream(); try{ processURLData(input); } finally { input.close(); } } protected abstract void processURLData(InputStream input) throws IOException; }
-
实现类
public class URLProcessorImpl extends URLProcessorBase { @Override protected void processURLData(InputStream input) throws IOException { int data = input.read(); while(data != -1){ System.out.println((char) data); data = input.read(); } } }
-
测试
URLProcessorImpl urlProcessor = new URLProcessorImpl(); urlProcessor.process(new URL("http://wjl.king.cn"));
2.5 短信发送案例1
-
短信发送系统,两层抽象,一层通用发短信,一层针对特定业务场景发短信。
-
涉及类
- cn.king.demo01.SmsHandler 接口
- cn.king.demo01.AbstractSmsHandler 抽象类
- cn.king.demo01.DefaultSmsHandler 实现类 通用场景发短信
- cn.king.demo01.XxxSmsHandler 实现类 针对Xxx业务场景发短信
- BaseRequest、SmsReq、XxxSmsReq;BaseResponse、SmsResp、XxxSmsResp 参数和返回值
- cn.king.demo01.Demo 作为测试类
-
cn.king.demo01.SmsHandler
/** * @author: wjl@king.cn * @version: 1.0.0 * @description: 短信处理器接口。定义功能、定义规范。 */ public interface SmsHandler<T, R> { // 前置方法。可以对参数进行处理 T before(T t); // 同步发送 R send(T t) throws Exception; // 异步发送 R asyncSend(T t) throws Exception; // 查询发送详情 R querySendDetail(T t) throws Exception; }
-
cn.king.demo01.AbstractSmsHandler
/** * @author: wjl@king.cn * @version: 1.0.0 * @description: 短信处理器抽象类。定义模板。 * 抽象类中的泛型进一步限制了泛型的范围, * 如果某个实现类实现了接口,那么两个泛型类型可以是任意, * 如果某个实现类继承了抽象类,那么两个泛型类型必须是BaseRequest或其子类 和 BaseResponse或其子类。 * <p> * 抽象类不实现任何业务相关的方法,只是定义模板、实现通用方法。 */ public abstract class AbstractSmsHandler<TT extends BaseRequest, RR extends BaseResponse> implements SmsHandler<TT, RR> { // 子类可以不实现前置方法,因此在此空实现。 @Override public TT before(TT tt) { return tt; } @Override public RR send(TT tt) throws Exception { TT req = before(tt); return doSend(req); } // 子类可以不实现同步发送的方法,因此在此空实现。 protected RR doSend(TT tt) { return null; } @Override public RR asyncSend(TT tt) throws Exception { new Thread(() -> { TT req = before(tt); doAsyncSend(req); }).start(); return null; } // 子类必须实现异步发送的方法,因此搞成抽象方法。 protected abstract void doAsyncSend(TT tt); // 查询方法没有模板。子类可以不实现查询方法,因此在此空实现。 @Override public RR querySendDetail(TT tt) throws Exception { return null; } }
-
cn.king.demo01.DefaultSmsHandler
/** * @author: wjl@king.cn * @version: 1.0.0 * @description: 默认的短信处理器。通用场景的短信处理器。 */ public class DefaultSmsHandler extends AbstractSmsHandler<SmsReq, SmsResp> { @Override protected SmsResp doSend(SmsReq smsReq) { System.out.println("同步发短信"); return new SmsResp(); } @Override protected void doAsyncSend(SmsReq smsReq) { System.out.println("异步发短信"); } @Override public SmsResp querySendDetail(SmsReq smsReq) throws Exception { System.out.println("查询某条短信的发送详情"); return new SmsResp(); } }
-
cn.king.demo01.XxxSmsHandler
/** * @author: wjl@king.cn * @version: 1.0.0 * @description: 针对Xxx业务场景的短信处理器。 */ public class XxxSmsHandler extends AbstractSmsHandler<XxxSmsReq,XxxSmsResp>{ @Override protected void doAsyncSend(XxxSmsReq xxxSmsReq) { System.out.println("针对Xxx业务实现的短信发送方法"); } }
-
Req、Resp
/** * @author: wjl@king.cn * @version: 1.0.0 * @description: 所有请求参数的基类 */ public class BaseRequest { } /** * @author: wjl@king.cn * @version: 1.0.0 * @description: 所有响应的基类 */ public class BaseResponse { } /** * @author: wjl@king.cn * @version: 1.0.0 * @description: 通用的短信请求参数 */ public class SmsReq extends BaseRequest{ } /** * @author: wjl@king.cn * @version: 1.0.0 * @description: 通用的短信返回值 */ public class SmsResp extends BaseResponse{ } /** * @author: wjl@king.cn * @version: 1.0.0 * @description: 针对Xxx业务场景的短信请求参数 */ public class XxxSmsReq extends SmsReq{ } /** * @author: wjl@king.cn * @version: 1.0.0 * @description: 针对Xxx业务场景的短信返回值 */ public class XxxSmsResp extends SmsResp { }
-
cn.king.demo01.Demo
public class Demo { public static void main(String[] args) throws Exception { XxxSmsHandler smsHandler = new XxxSmsHandler(); smsHandler.asyncSend(new XxxSmsReq()); } }
2.6 短信发送案例2
-
和【2.5】需求相同。结合Spring。
-
涉及类
- com.king.handler.sms.SmsHandler 接口 定义功能、规范
- com.king.handler.sms.AbstractSmsHandler 抽象类 定义模板
- com.king.handler.sms.DefaultSmsHandler 默认实现 处理通用场景
- com.king.handler.sms.InactiveUserSmsHandler 专门处理”给未激活用户发短信“的场景
- com.king.application.SmsApplication 聚合业务层 聚合短信业务
- com.king.controller.SmsApiControllerImpl 接口实现层
- com.king.controller.SmsApiController 接口层 供接口实现层实现、Feign实现
-
com.king.controller.SmsApiController
@Api(tags = "短信接口") @RequestMapping("/king/sms/api") public interface SmsApiController { @PostMapping("/inactive/send") @ApiOperation(value = "向未激活用户发送短信", notes = "向未激活用户发送短信", hidden = false) AjaxResult<Void> inactiveUserSmsSend(InactiveUserSmsReq req); }
-
com.king.controller.SmsApiControllerImpl
@Slf4j @RestController public class SmsApiControllerImpl extends BaseController implements SmsApiController { @Resource private SmsApplication smsApplication; // 此处Controller的实现写法可以忽略,无非也是模板模式,对smsApplication.inactiveUserSmsSend(req);的返回值进行了处理后响应给客户端而已 @Override public AjaxResult<Void> inactiveUserSmsSend(InactiveUserSmsReq req) { return executeWithLogContext(req, new BaseController.BizCallbackWithoutResult<InactiveUserSmsReq>() { @Override protected void doInTransactionWithoutResult(InactiveUserSmsReq req) throws Exception { smsApplication.inactiveUserSmsSend(req); } }); } }
-
com.king.application.SmsApplication
@Slf4j @Component public class SmsApplication { // AcsResponse为阿里云短信发送API的返回值 @Resource(name = "inactiveUserSmsHandler") private SmsHandler<SmsReq, AcsResponse> smsHandler; @Resource private SmsAService smsAService; @Resource private SmsBService smsBService; /** * @author: wjl@king.cn * @param: req * @return: void * @description: 向未激活用户发送短信 */ public void inactiveUserSmsSend(InactiveUserSmsReq req) throws Exception { if (req == null || CollectionUtils.isEmpty(req.getPhoneList())) { return; } smsHandler.asyncSend(req); } }
-
com.king.handler.sms.SmsHandler
public interface SmsHandler<T, R> { /** * 前置处理 * * @param t * @throws Exception */ T before(T t) throws Exception; /** * 同步发送 * * @param t * @return * @throws Exception */ R send(T t) throws Exception; /** * 异步发送 * * @param t * @return * @throws Exception */ void asyncSend(T t) throws Exception; /** * 查询发送结果详情 * * @param t * @return * @throws Exception */ R query(T t) throws Exception; }
-
com.king.handler.sms.AbstractSmsHandler
// AcsResponse 为阿里云短信发送API的返回值。不要被名字吓到,只不过是一个返回值而已。 @Slf4j public abstract class AbstractSmsHandler<T extends BaseRequest, R extends AcsResponse> implements SmsHandler<T, R> { @Override public T before(T t) { return t; } @Override public void asyncSend(T t) throws Exception { CommonAsyncExecutor.execute(() -> { T req = this.before(t); this.doAsyncSend(req); }); } protected abstract void doAsyncSend(T t); @Override public R send(T t) throws Exception { T req = this.before(t); return this.doSend(req); } protected R doSend(T t) { return null; } @Override public R query(T t) throws Exception { return null; } }
-
com.king.handler.sms.DefaultSmsHandler
@Slf4j @Component public class DefaultSmsHandler extends AbstractSmsHandler<SmsReq, AcsResponse> { // 阿里云短信发送工具类 @Resource private AliSmsHelper aliSmsHelper; @Override protected void doAsyncSend(SmsReq req) { if (req == null || CollectionUtils.isEmpty(req.getPhoneList())) { return; } aliSmsHelper.sendSms(req.getPhoneList(), req.getSignName(), req.getTemplateCode(), req.getTemplateParam()); } @Override protected AcsResponse doSend(SmsReq req) { if (req == null || CollectionUtils.isEmpty(req.getPhoneList())) { return null; } return aliSmsHelper.sendSms(req.getPhoneList(), req.getSignName(), req.getTemplateCode(), req.getTemplateParam()); } @Override public AcsResponse query(SmsReq req) throws Exception { if (req == null || CollectionUtils.isEmpty(req.getPhoneList())) { return null; } return aliSmsHelper.querySendDetail(req.getPhoneList(), req.getBizId()); }
-
com.king.handler.sms.InactiveUserSmsHandler
@Slf4j @Component public class InactiveUserSmsHandler extends AbstractSmsHandler<InactiveUserSmsReq, AcsResponse> { // 阿里云短信发送工具类 @Resource private AliSmsHelper aliSmsHelper; @Resource private OpenApiProperties openApiProperties; @Resource private UserRemote userFeign; @Override protected void doAsyncSend(InactiveUserSmsReq req) { if (req == null || CollectionUtils.isEmpty(req.getPhoneList())) { return; } // 短信签名 String smsSignName = openApiProperties.getSmsSignName(); // 短信模板 String smsTemplateCodePromotion = openApiProperties.getSmsTemplateCode(); List<String> phoneList = req.getPhoneList(); for (String phone : phoneList) { // 业务处理。。。 aliSmsHelper.sendSms(phone, smsSignName, smsTemplateCode, templateParam); } } }
3. 回过头来看概念
- 见【1.概念】