抽象类与模板方法

如何学习本文

  • 先对"概念(理论)“有大概印象,再将大量的"案例(实践)“看懂,如果看不懂可以抄写,多抄几遍就懂了。代码案例弄懂了之后倒回去看概念,你会发现概念描述的很清晰很准确。然后自己给自己提出一个需求,用本文的知识实现这个需求。自己写一篇关于"抽象类和模板方法设计模式"的博客或者笔记,最好是"脑图+文字+画图+代码”,笔记上传到云端,代码上传到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.概念】
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值