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

引言

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

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

这里就引发了利用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 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 noticeHandlerBeans = applicationContext.getBeansOfType(PointHandlerService.class);
        Collection 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 {

    /**
     * 增加分值
     * @return
     */
    boolean addPoint(T obj);

。。。省略后面代码
}

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

@Component
public class PointLoginSignServiceImpl extends PointBaseSeriveImpl implements PointHandlerService {
    @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模式的运用与实现)