代码精进之路-设计模式-组合模式

ea120109cf4b1553396b034830649001.png

组合模式属于设计模式中的"结构型模式"。

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

你可以使用它将对象组合成树状结构,并且能像使用独立对象一样使用他们。

组合模式使用递归方式处理对象树中的所有项目。

接口和类的组合方式:

7a94fb3c2136e3415bd6bb6ec75fd93c.png

1、组件component接口描述了树中树枝和树叶的共有的操作;

2、树叶leaf是末级节点,不包含下级,主要处理逻辑一般在树叶中;

3、树枝composite是包含其它树叶和树枝的容器,通过组件component接口与下级通信;
 

组合模式两种实现方式:

1、透明方式

5d7c17a0283477273b296bc767df48ff.png

树枝和树叶拥有共同的行为,虽然树叶中的add、remove、display 行为没有意义,可以保持空或者抛出异常。

这样做的好处是叶节点和枝节点对于外界没有区别,它们具备完全一致的接口。

2、安全方式

c98af1303891616c0e352597b7c0bb71.png

维护下级节点的相关方法只出现在树枝中。

叶节点无需在实现Add与Remove这样的方法,但是对于客户端来说,必须对叶节点和枝节点进行判定,为客户端的使用带来不便。

应用场景:

对象之间呈现树状结构、金字塔结构。

想表达“部分-整体”层次结构(树形结构)时。

希望用户忽略组合对象与单个对象的不同,用户将统一的使用组合结构中的所有对象。

应用案例:

1、公司组织结构

组织结构:根节点: 董事长,下级:副董事长,总裁,下级:CEO、CTO、CFO

下级:研发部、销售部、市场部、财务部、人力部。。。

整体成树状结构

3a5123031837b969005192320a4f70b9.png

2、计算订单总价

订单中的项目可能包含几个具体的商品,也可能包括一个套餐,套餐中也可能包含一个具体商品,也可能包括另一个套餐,。。。整体成树状结构分布

f28edbacbc24b6d603bf46ce245bd1e9.png

3、国家省市直辖市自治区分布

1464aeef40014a8c6f5329549668e388.png

开源框架案例:
 

Spring中WebMvcConfigurerComposite

class WebMvcConfigurerComposite implements WebMvcConfigurer {


    //下级节点集合
   private final List<WebMvcConfigurer> delegates = new ArrayList<>();




   public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
      if (!CollectionUtils.isEmpty(configurers)) {
         this.delegates.addAll(configurers);
      }
   }




   @Override
   public void configurePathMatch(PathMatchConfigurer configurer) {
      for (WebMvcConfigurer delegate : this.delegates) {
      //递归
         delegate.configurePathMatch(configurer);
      }
   }


   @Override
   public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureContentNegotiation(configurer);
      }
   }


   @Override
   public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureAsyncSupport(configurer);
      }
   }


   @Override
   public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureDefaultServletHandling(configurer);
      }
   }


   @Override
   public void addFormatters(FormatterRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addFormatters(registry);
      }
   }


   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addInterceptors(registry);
      }
   }


   @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addResourceHandlers(registry);
      }
   }


   @Override
   public void addCorsMappings(CorsRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addCorsMappings(registry);
      }
   }


   @Override
   public void addViewControllers(ViewControllerRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addViewControllers(registry);
      }
   }


   @Override
   public void configureViewResolvers(ViewResolverRegistry registry) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureViewResolvers(registry);
      }
   }


   @Override
   public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addArgumentResolvers(argumentResolvers);
      }
   }


   @Override
   public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.addReturnValueHandlers(returnValueHandlers);
      }
   }


   @Override
   public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureMessageConverters(converters);
      }
   }


   @Override
   public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.extendMessageConverters(converters);
      }
   }


   @Override
   public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.configureHandlerExceptionResolvers(exceptionResolvers);
      }
   }


   @Override
   public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
      for (WebMvcConfigurer delegate : this.delegates) {
         delegate.extendHandlerExceptionResolvers(exceptionResolvers);
      }
   }


   @Override
   public Validator getValidator() {
      Validator selected = null;
      for (WebMvcConfigurer configurer : this.delegates) {
         Validator validator = configurer.getValidator();
         if (validator != null) {
            if (selected != null) {
               throw new IllegalStateException("No unique Validator found: {" +
                     selected + ", " + validator + "}");
            }
            selected = validator;
         }
      }
      return selected;
   }


   @Override
   @Nullable
   public MessageCodesResolver getMessageCodesResolver() {
      MessageCodesResolver selected = null;
      for (WebMvcConfigurer configurer : this.delegates) {
         MessageCodesResolver messageCodesResolver = configurer.getMessageCodesResolver();
         if (messageCodesResolver != null) {
            if (selected != null) {
               throw new IllegalStateException("No unique MessageCodesResolver found: {" +
                     selected + ", " + messageCodesResolver + "}");
            }
            selected = messageCodesResolver;
         }
      }
      return selected;
   }


}

Dubbo中CompositeConfiguration;

public class CompositeConfiguration implements Configuration {
    private Logger logger = LoggerFactory.getLogger(CompositeConfiguration.class);


    /**
     * List holding all the configuration
     * 各类配置集合
     */
    private List<Configuration> configList = new LinkedList<Configuration>();


    public CompositeConfiguration() {


    }


    public CompositeConfiguration(Configuration... configurations) {
        if (configurations != null && configurations.length > 0) {
            Arrays.stream(configurations).filter(config -> !configList.contains(config)).forEach(configList::add);
        }
    }


    public void addConfiguration(Configuration configuration) {
        if (configList.contains(configuration)) {
            return;
        }
        this.configList.add(configuration);
    }


    public void addConfigurationFirst(Configuration configuration) {
        this.addConfiguration(0, configuration);
    }


    public void addConfiguration(int pos, Configuration configuration) {
        this.configList.add(pos, configuration);
    }


    @Override
    public Object getInternalProperty(String key) {
        Configuration firstMatchingConfiguration = null;
        for (Configuration config : configList) {
            try {
                if (config.containsKey(key)) {
                    firstMatchingConfiguration = config;
                    break;
                }
            } catch (Exception e) {
                logger.error("Error when trying to get value for key " + key + " from " + config + ", will continue to try the next one.");
            }
        }
        if (firstMatchingConfiguration != null) {
            return firstMatchingConfiguration.getProperty(key);
        } else {
            return null;
        }
    }


    @Override
    public boolean containsKey(String key) {
        //这里体现了递归思想
        return configList.stream().anyMatch(c -> c.containsKey(key));
    }
}

mybatis 的MixedSqlNode、ChooseSqlNode;

JDK 的 AWT(Abstract Window Toolkit),使用了组合模式,AWT 中包含了两种组件:容器组件和基本组件;

Spring中CompositeCacheManager;

业务开发应用:

数据迁移系统,一个迁移Job是一个顶级任务,Job下面可以增加要迁移的中介Agency,Agency下面有自己管理的企业Org,这个Job的目标是将中介下面所有企业数据从a数据库迁移到b数据库。

代码原型:

1、抽象一个任务接口

/**
 * @Project fighting-core
 * @Description 迁移任务
 * @Author lvaolin
 * @Date 2021/12/29 下午19:49
 */
public interface IMigrationTask {
    /**
     * 迁移启动
     */
    void startMove();


    /**
     * 迁移耗时统计
     * @return
     */
    long costTime();




    void add(IMigrationTask migrationTask);


    void remove(IMigrationTask migrationTask);
}

2、实现一个无差异的树枝任务和树叶任务

/**
 * @Project fighting-core
 * @Description 迁移节点
 * @Author lvaolin
 * @Date 2021/12/29 下午19:49
 */
@Data
public class MigrationNode implements IMigrationTask{
    private String orgId;
    private List<IMigrationTask> migrationNodeList = new ArrayList<>();
    private long costTime = 0;


    public MigrationNode(){
    }
    public MigrationNode(String orgId){
        this.orgId = orgId;
    }


    @Override
    public void startMove() {
        if (migrationNodeList.size()>0) {
            //容器节点
            for (IMigrationTask migrationNode : migrationNodeList) {
                migrationNode.startMove();
            }
        }else{
            costTime = new Random().nextInt(100);
            //叶子节点
            System.out.println("企业"+orgId+"开始迁移,耗时:"+costTime);




        }
    }


    @Override
    public long costTime() {


        if (migrationNodeList.size()>0) {
            //容器节点
            for (IMigrationTask migrationNode : migrationNodeList) {
                costTime += migrationNode.costTime();
            }
        }
        return costTime;
    }


    @Override
    public void add(IMigrationTask migrationTask) {
        migrationNodeList.add(migrationTask);
    }


    @Override
    public void remove(IMigrationTask migrationTask) {
        migrationNodeList.remove(migrationTask);
    }




}

上下文模拟

/**
 * @Project fighting-core
 * @Description 上下文模拟
 * @Author lvaolin
 * @Date 2021/12/29 下午19:49
 */
public class Main {


    public static void main(String[] args) {
        //顶级任务
        MigrationNode job = new MigrationNode();


        //二级任务
        MigrationNode agency01 = new MigrationNode();
        MigrationNode agency02 = new MigrationNode();


        //三级任务
        MigrationNode org0101 = new MigrationNode(UUID.randomUUID().toString());
        MigrationNode org0102 = new MigrationNode(UUID.randomUUID().toString());
        MigrationNode org0103 = new MigrationNode(UUID.randomUUID().toString());


        MigrationNode org0201 = new MigrationNode(UUID.randomUUID().toString());
        MigrationNode org0202 = new MigrationNode(UUID.randomUUID().toString());
        MigrationNode org0203 = new MigrationNode(UUID.randomUUID().toString());


        //任务组合
        job.add(agency01);
        job.add(agency02);


        //任务组合
        agency01.add(org0101);
        agency01.add(org0102);
        agency01.add(org0103);
        //任务组合
        agency02.add(org0201);
        agency02.add(org0202);
        agency02.add(org0203);


        //启动任务
        job.startMove();
        //统计耗时
        System.out.println("总耗时统计:"+job.costTime());


    }
}

结果

26775b4fec032c6af7d160226857ed5d.png

688c50907341497bfe327bef304fffa7.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕哥架构

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值