设计模式原理及应用·组合模式
前言
在互联网系统中,Java语言大行其道。越来越多的开源框架,商业框架应用在web项目中,越来越多的组件被创建,大大提高了网站开发效率,使得开发者越发的可以专注于业务逻辑而非系统辅助组件的实现。
但是,在有了框架的帮助之后,许多人容易产生误解,框架提供的功能强大,我们不需要写很多的辅助功能,专注于业务的时候没机会,或者不需要使用设计模式。
这里有个观点,写业务没什么技术含量,也不需要什么高深的模式。对于这个想法,业务同学表示,这个锅...它不背。
一、原理
1.1 介绍
组合模式通常的作用:定义整体与部分,并定义集体动作。很多资料也会将其描述为“主干”和“叶子”,这是从树形结构上来进行描述。
- 定义“人在吃饭”。整体是人,集体动作是”吃饭“,部分是需要配合的手和嘴,动作是“吃饭”。
- 定义“施工队修路”。整体是施工队,集体动作是”修路“,部分是施工队中的人,器械,等等,动作是”修路“。
- 还有很多,以一个整体对外暴露接口,但是每个部分实现接口的方式各不一样。
1.2 结构图
组合模式的组成:
- 集体动作:如图BaseOperation,集体动作是doOperate()。
- 部分1:ComponentOperator1,它实现了doOperate()。
- 部分2:ComponentOperator2,它实现了doOperate()。
- 整体:CompositeOperator,它将各个部分组合起来,并以整体的身份执行doOperate();
关于compositeOperator怎么将各个组件部分给串联起来作为整体,其实从上面UML图可知,使用的是list,里面保存了所有BaseOperation的实现类,在运行时,将循环执行,后续在例子中会详细看到它的运行机制。
规范的命名可以帮助我们更好的了解程序,例如:我们去spring源码中搜索”composite...“将得到很多的类,他们都是使用组合模式,而且我们可以通过查看他们实现的接口和定义的List
二、应用
组合模式算是应用比较多的设计模式之一,我们很容易就在框架中找到它,也很容易在业务系统中找到它的应用场景。
2.1 spring框架
首先:在spring源码中搜索类“composite”。我们可以得到:
相逢即是缘,我们分析下CompositeCacheManager中,组合模式的实现方式。
首先要有以下概念:
- 既然使用了Composite*的命名,以框架的命名规范来讲,这必然是组合模式中的组合组件(将各个组件组合到一起);
- 组合组件中,必然有List
,T是接口规范; - 组合组件也是“集体动作”的实现类。
关系
UML图
CompositeCacheManager如何实现接口方法
private final List<CacheManager> cacheManagers = new ArrayList<>();
...
...
// 直接遍历list,返回所有的cache
@Override
@Nullable
public Cache getCache(String name) {
for (CacheManager cacheManager : this.cacheManagers) {
Cache cache = cacheManager.getCache(name);
if (cache != null) {
return cache;
}
}
return null;
}
@Override
public Collection<String> getCacheNames() {
Set<String> names = new LinkedHashSet<>();
for (CacheManager manager : this.cacheManagers) {
names.addAll(manager.getCacheNames());
}
return Collections.unmodifiableSet(names);
}
小结
在spring上例中,对于组合模式的实现,算是比较经典的,没有多余的操作,组合模式的结构和功能均得以体现。
分析spring源码技巧一:了解设计模式,如果在源码阅读中,看到设计模式,先去看看设计模式的用法以及组件,再联系spring的功能,分析每个方法,每个属性的作用。既学习了设计模式的用法,又能从设计的角度理解spring源码。
2.2 业务场景
需求:
在api系统中,需要安全模块对于外部请求进行安全校验。现在需要校验3个模块。分别是请求签名校验,ip白名单对应,客户端权限校验。
分析:采用组合模式
- 集体动作:校验是否通过
- 组件1:签名校验
- 组件2:白名单校验
- 组件3:客户权限校验
程序设计:
**程序实现:**
- AOP进行精确拦截,(过滤器,拦截器皆可,根据实际业务需求)。
- 定义context传递上下文,并将每个组件处理的“拒绝原因”写入上下文。
- 定义manager调用CompositeComponentAuthority的permit()方法。
核心代码:
manager管理器
private CompositeComponentAuthority authority;
/**
* 校验权限
* @param joinPoint
*/
public Object checkAuthority(ProceedingJoinPoint joinPoint) throws Throwable {
// 1.获取请求上下文
ParamContext paramContext = this.getParamContext(joinPoint);
// 2.去鉴权
boolean permission = authority.permit(paramContext);
// 3.鉴权不通过则直接返回
if (!permission) {
return paramContext.getReturnMap();
}
// 4.放行
return joinPoint.proceed();
}
CompositeComponentAuthority核心
/**
* autowired可以注入所有的<T>的实现类,并根据实现类的Order进行排序
*/
@Autowired(required = false)
private List<ApiAuthority> items = new LinkedList<>();
@Override
public Boolean permit(ParamContext context) {
if (CollectionUtils.isEmpty(items)) {
return true;
}
boolean permision;
for (ApiAuthority authority : items) {
permision = authority.permit(context);
if (!permision){
return permision;
}
}
return true;
}
小结:
组合模式还有很多用途,不一一举例,在我服务的公司里面,在现有系统,就不止一处业务使用到组合模式。它的应用场景还是很多的,我们只需要理解“整体-部分”的概念,最好再看看框架中他们如何被实现,在写业务时,你也可以设计出更好的结构。
凡你能说的,你说清楚。凡你不能说的,留给沉默!