美文网首页
关于轻设计模式doHandler模式的运用与实现

关于轻设计模式doHandler模式的运用与实现

作者: YukunWen | 来源:发表于2018-11-23 16:37 被阅读39次

    引言

    在实际的开发中,很有可能我们写好了很多的模块,但是突然要增加一个入侵很多接口的需求。比如,本来正常的登陆,发布文章,分享文章,是很常见的基本功能。但是老板突然有一点来说,我们要留存用户,要增加一些积分规则和活动,登录的时候我们要给它加分;鼓励用户发文章,发文章我们要给他们加分等等。

    那么如何针对这种同一个入口进入,且实现的内容不完全相同的模式进行处理呢?

    这里就引发了利用spring上下文实现的轻设计模式

    doHandler模式的分析与实现

    下面的内容都会按照引言中的业务需求来进行逐步实现。

    一、 整体考虑

    首先有各种各样的加分规则,那么可以先采用一个枚举类去保存加分的编号和分值

    @AllArgsConstructor
    @Getter
    public enum PointEnum {
        /**
         * 编号1:发布文章,增加2分
         */
        PUBLISH_ARTICLE(1,2),
        /**
         * 编号2:分享文章,增加3分
         */
        SHARE_ARTICLE(2,3),
        /**
         * 编号3:登录签到,增加1分
         */
        LOGIN_SIGN(3,1),
        ;
        /**
         * 编号
         */
        private Integer code;
        /**
         * 分值
         */
        private Integer point;
    }
    

    其次,还要有一个统一的service书写抽象的addPoint()接口。同时,为了后面能区别不同的分值实现,这个service应该还要具有一个getCode()方法。

    public interface PointHandlerService {
    
        /**
         * 增加分值
         * @return
         */
        boolean addPoint();
    
        /**
         * 返回的是不同情况下枚举的值
         * @return
         */
        Integer getCode();
    }
    
    二、 spring InitializingBean和 ApplicationContextAware的运用

    我们要在spring容器初始化的时候,要将PointHandlerService 的不同实现都要加入到容器中,同时我们要内部维护一个Map用来存储它的实现,以便我们能快捷的取到。

    @Component
    public class PointHandlerInterceptor implements InitializingBean, ApplicationContextAware {
    
        /**
         * 内部维护的Map
         */
        private Map<Integer, PointHandlerService> pointHandlers = new HashMap<>();
    
        private ApplicationContext applicationContext;
    
        /**
         * 公共的方法
         * @param type
         * @return
         */
        public Boolean pointAdd(Integer type){
            PointHandlerService pointHandlerService = pointHandlers.get(type);
            return pointHandlerService.addPoint();
        }
    
        /**
         * 初始化pointHandlers这个Map
         * @throws Exception
         */
        @Override
        public void afterPropertiesSet() throws Exception {
            Map<String, PointHandlerService> noticeHandlerBeans = applicationContext.getBeansOfType(PointHandlerService.class);
            Collection<PointHandlerService> values = noticeHandlerBeans.values();
            for (PointHandlerService noticeHandler : values) {
                Integer type = noticeHandler.getCode();
                pointHandlers.put(type, noticeHandler);
            }
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

    这样子对于内部的函数采用pointHandlers.get(type)就可以获得相应的PointHandlerService 的实现类。

    三、 实现类简单demo
    @Component
    public class PointLoginSignServiceImpl extends PointBaseSeriveImpl implements PointHandlerService {
        @Override
        public boolean addPoint() {
            //这里写上要处理的业务逻辑
            System.out.println("登录加分:"+PointEnum.LOGIN_SIGN.getPoint());
            return Boolean.TRUE;
        }
    
        @Override
        public Integer getCode() {
            return PointEnum.LOGIN_SIGN.getCode();
        }
    }
    
    @Component
    public class PointPublishArticleServiceImpl  extends PointBaseSeriveImpl implements PointHandlerService {
        @Override
        public boolean addPoint() {
            //这里写上要处理的业务逻辑
            System.out.println("发布文章加分:"+PointEnum.PUBLISH_ARTICLE.getPoint());
            return Boolean.TRUE;
        }
    
        @Override
        public Integer getCode() {
            return PointEnum.PUBLISH_ARTICLE.getCode();
        }
    }
    

    撰写测试方法

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class HandlerdemoApplicationTests {
    
        @Autowired
        private PointHandlerInterceptor pointHandlerInterceptor;
    
        @Test
        public void contextLoads() {
            pointHandlerInterceptor.pointAdd(PointEnum.LOGIN_SIGN.getCode());
            pointHandlerInterceptor.pointAdd(PointEnum.PUBLISH_ARTICLE.getCode());
            pointHandlerInterceptor.pointAdd(PointEnum.SHARE_ARTICLE.getCode());
        }
    }
    

    最后结果如下:

    图1.结果图

    拓展与优化

    1. 在pointHandlers.get(type)的时候可以直接返回service对象。这样子做的好处在于,拿到子类的service,以便可能会调用其他方法。

    2.service层可能要传入一些BO对象,此时可以修改成如下写法:

    public interface PointHandlerService<T> {
    
        /**
         * 增加分值
         * @return
         */
        boolean addPoint(T obj);
    
    。。。省略后面代码
    }
    

    在实现的时候写明是哪个BO对象,有利于利用java强对象规则在编译阶段进行校验。另外serviceImpl也可以抽取一个公共的类进行继承的形式。

    @Component
    public class PointLoginSignServiceImpl extends PointBaseSeriveImpl implements PointHandlerService<LoginSignBO> {
        @Override
        public boolean addPoint(LoginSignBO loginSignBO) {
            //这里写上要处理的业务逻辑
            System.out.println("登录加分:"+PointEnum.LOGIN_SIGN.getPoint());
            return Boolean.TRUE;
        }
    
    。。。省略后面的代码
    

    BO对象也可以抽取出一些公共的参数与让各个不同的BO对象继承。

    1. 第二点说的是基于公共的抽取出来。那么有的时候可能会在一些实现类里面增加一些方法,其他的实现类不需要。比如说,在发布文章和分享文章完成后,要对文章的分享数进行一个统计,而登陆就不需要。
      当然可以采取在发布文章和分享文章的实现类里面实现,但是这样子类的一个方法业务逻辑就不够纯正。另外有可能这个需求是一时的,比如在推广的时候这个统计非常必须,到后面会干掉的。直接修改实现类内部未免入侵性过大。
      这个时候可以采取在service增加一个额外的接口,但是这个接口是default类型的!
        /**
         * 并不是必须的实现
         */
        default boolean notNeedImpl(){
            System.out.println("I am the PointHandlerService");
            return Boolean.TRUE;
        }
    

    这是java8的特性,接口也可以写默认的实现方法。这样子在一些子类有所不同的时候,只要复写该方法,就会调用实现类的,而不是接口的了。测试代码如下:

        @Test
        public void contextLoads() {
            PointHandlerService p1 = pointHandlerInterceptor.pointAdd(PointEnum.LOGIN_SIGN.getCode());
            p1.addPoint(new LoginSignBO());
            p1.notNeedImpl();
            PointHandlerService p2 =pointHandlerInterceptor.pointAdd(PointEnum.PUBLISH_ARTICLE.getCode());
            p2.addPoint(new PublishArticleBO());
            p2.notNeedImpl();
            PointHandlerService p3 =pointHandlerInterceptor.pointAdd(PointEnum.SHARE_ARTICLE.getCode());
            p3.addPoint(new ShareArticleBO());
            p3.notNeedImpl();
        }
    

    最终实现结果如下:

    图2.测试代码结果

    以上代码均放在了我的github博客上面,欢迎各位读者下载查阅。

    https://github.com/YukunWen/HandlerDemo

    感谢阅读,如有帮助,烦请点赞,谢谢!

    相关文章

      网友评论

          本文标题:关于轻设计模式doHandler模式的运用与实现

          本文链接:https://www.haomeiwen.com/subject/vkejqqtx.html