引言
在实际的开发中,很有可能我们写好了很多的模块,但是突然要增加一个入侵很多接口的需求。比如,本来正常的登陆,发布文章,分享文章,是很常见的基本功能。但是老板突然有一点来说,我们要留存用户,要增加一些积分规则和活动,登录的时候我们要给它加分;鼓励用户发文章,发文章我们要给他们加分等等。
那么如何针对这种同一个入口进入,且实现的内容不完全相同的模式进行处理呢?
这里就引发了利用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.结果图拓展与优化
- 在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对象继承。
- 第二点说的是基于公共的抽取出来。那么有的时候可能会在一些实现类里面增加一些方法,其他的实现类不需要。比如说,在发布文章和分享文章完成后,要对文章的分享数进行一个统计,而登陆就不需要。
当然可以采取在发布文章和分享文章的实现类里面实现,但是这样子类的一个方法业务逻辑就不够纯正。另外有可能这个需求是一时的,比如在推广的时候这个统计非常必须,到后面会干掉的。直接修改实现类内部未免入侵性过大。
这个时候可以采取在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博客上面,欢迎各位读者下载查阅。
感谢阅读,如有帮助,烦请点赞,谢谢!
网友评论