设计模式原理及应用·组合模式

前言

在互联网系统中,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;
              }

小结:
组合模式还有很多用途,不一一举例,在我服务的公司里面,在现有系统,就不止一处业务使用到组合模式。它的应用场景还是很多的,我们只需要理解“整体-部分”的概念,最好再看看框架中他们如何被实现,在写业务时,你也可以设计出更好的结构。

posted @ 2019-06-18 16:49  undifinedException  阅读(1054)  评论(0编辑  收藏  举报