Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.
  将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对某个对象和组合对象的使用具有一致性。

  这句话看起来似乎不太好理解,我们可以暂时先有一个概念。接下来我们会通过具体的案例,来直观的感受什么是组合模式。
假如你是一下小型IT公司的老板,整个公司处于初创阶段,只有4个部门,每个部门各司其职,负责对外承接研发工作。
  –金融
  –文娱
  –旅游
  –海淘
  你的任务,就是将具体的需求分发给不同的部门,所以你很容易写出了这样一段代码。
  1. if(type == 金融){
  2. //..
  3. }else if(type == 文娱){
  4. //..
  5. }
  6. else if(type == 旅游){
  7. //..
  8. }else{
  9. //..
  10. }
  经过你的不断努力,一段时间过后,公司已经初具规模,这个时候公司的体系架构也变得复杂起来:
公司分为不同的事业群、每个事业群下有不同的战区、每个战区下又有一级部门、一级部门下有二级部门等等等等…,这个时候,你会发现,如果你还按照原来的方法去编写代码,你的代码可能会变成这样:
  (这种代码又叫做箭头形代码,有很多优化方法,比如提取方法,提前return..等等,日后会出一个代码规范化、优化的博客)
回过头来,我们再来分析这个问题,我们希望的是,不同部门,各司其职,这个时候我们就可以考虑采用组合模式
每个部门其实都是由一个个子部门组成,子部门和部门的关系,其实就是我们定义中所说的“整体-部分”的关系,如果我们把公司当作一颗树的根节点,那么各个部门就组成了这颗树的枝和叶,这样部门之间就有了树的层级结构。

  而定义中描述的:使得用户对某个对象和组合对象的使用具有一致性,其实就是我们可以通过协议(编程语言中的抽象类或接口)来定义部门的属性和行为,这样无论是对于哪个部门,他对外的操作都是一致的,整体和部分就具有了一致性。

  首先整体是由部分组成的, 每个部分其实可以作为其部分的整体,递归下去,所以我们代码中调用部分的地方,都可以用整体来替换。
(比如CTO体系/企业应用研发部/企业信息化部,CTO体系是一个整体,企业应用研发部是部分,但是对于企业信息化部来说,企业应用研发部其实也是一个整体)
其次如果整体和部分具有一致性,那么对于我们来说,将不用去书写代码去区分不同的部分了,而是可以以一种统一的方式去处理。
  组合模式在代码具体实现上,有两种不同的方式:
  1.透明组合模式:把组合(树节点)使用的方法放到统一行为(Component)中,让不同层次(树节点,叶子节点)的结构都具备一致行为;
  2.安全组合模式:统一行为(Component)只规定系统各个层次的最基础的一致行为,而把组合(树节点)本身的方法(管理子类对象的添加,删除等)放到自身当中;

  首先就是透明模式,会引入不必要的依赖,比如叶子结点其实是不需备add和remove功能的。所以才有了安全模式,将节点本身的方法放到自身维护,这样做的缺点就是各个节点间是有差异的,没有办法用完全统一的方式去处理。虽然实现不同,但是它们都遵循组合模式的规则。
  其实组合模式是一种反范式的设计模式,比如透明模式会引入不必要依赖的行为,违反了接口隔离原则, 安全模式区分了叶子结点和树枝节点,导致客户端没有办法依赖抽象,违反了依赖倒置原则,基类也不能用子类去替换,违反了里式替换原则。
  接下来我们来实现透明模式组合模式的代码:
  1. /**
  2. * 组合模式协议.
  3. *
  4. * @author jialin.li
  5. * @date 2019-12-20 15:22
  6. */
  7. public abstract class AbstractComponent {
  8. String name;
  9. public AbstractComponent(String name) {
  10. this.name = name;
  11. }
  12. public abstract void add(AbstractComponent c);
  13. public abstract void remove(AbstractComponent c);
  14. public abstract void display(int depth);
  15. }
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. /**
  4. * 组合模式中的枝叶,用来存储子部件.
  5. *
  6. * @author jialin.li
  7. * @date 2019-12-20 15:28
  8. */
  9. public class Composite extends AbstractComponent {
  10. private List<AbstractComponent> children = new ArrayList<>();
  11. public Composite(String name) {
  12. super(name);
  13. }
  14. @Override
  15. public void add(AbstractComponent c) {
  16. children.add(c);
  17. }
  18. @Override
  19. public void remove(AbstractComponent c) {
  20. children.remove(c);
  21. }
  22. @Override
  23. public void display(int depth) {
  24. for (int i = 0; i < depth; i++) {
  25. System.out.print("-");
  26. }
  27. System.out.println(name);
  28. for (AbstractComponent component : children) {
  29. component.display(depth + 1);
  30. }
  31. }
  32. }
  1. /**
  2. * 组合模式中的叶子节点.
  3. *
  4. * @author jialin.li
  5. * @date 2019-12-20 15:26
  6. */
  7. public class Leaf extends AbstractComponent {
  8. public Leaf(String name) {
  9. super(name);
  10. }
  11. @Override
  12. public void add(AbstractComponent c) {
  13. System.out.println("Cannot add a leaf!");
  14. }
  15. @Override
  16. public void remove(AbstractComponent c) {
  17. System.out.println("Cannot remove a leaf!");
  18. }
  19. @Override
  20. public void display(int depth) {
  21. for (int i = 0; i < depth; i++) {
  22. System.out.print("-");
  23. }
  24. System.out.println(name);
  25. }
  26. }
  1. /**
  2. * 客户端.
  3. *
  4. * @author jialin.li
  5. * @date 2019-12-20 15:33
  6. */
  7. public class Main {
  8. public static void main(String[] args) {
  9. Composite root = new Composite("root");
  10. root.add(new Leaf("Leaf A"));
  11. root.add(new Leaf("Leaf B"));
  12. Composite comp = new Composite("Composite X");
  13. comp.add(new Leaf("Leaf XA"));
  14. comp.add(new Leaf("Leaf XB"));
  15. root.add(comp);
  16. Composite comp2 = new Composite("Composite XY");
  17. comp2.add(new Leaf("Leaf XYA"));
  18. comp2.add(new Leaf("Leaf XYB"));
  19. comp.add(comp2);
  20. root.add(new Leaf("Leaf C"));
  21. Leaf d = new Leaf("D");
  22. root.add(d);
  23. root.remove(d);
  24. root.display(1);
  25. }
  26. }
  1. -root
  2. --Leaf A
  3. --Leaf B
  4. --Composite X
  5. ---Leaf XA
  6. ---Leaf XB
  7. ---Composite XY
  8. ----Leaf XYA
  9. ----Leaf XYB
  10. --Leaf C
  tomcat中的容器设计,也用到了组合模式:
tomcat中设计了4中容器:Engine、Host、Context、Wrapper。这四种容器的关系如下:
  它们之间是父子关系,除了Engine容器,其他都可以具有多个。容器是一个树状的层级结构
engine表示引擎,一个Service最多只能有一个引擎,Host表示虚拟主机、Context表示一个Web应用、Wrapper表示一个Servlet(后三种容器可以有多个)
  Tomcat 的server.xml配置文件中的标签,其实就代表了tomcat的层级结构:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Server port="8005" shutdown="SHUTDOWN">
  3. <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  4. <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  5. <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  6. <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  7. <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  8. <GlobalNamingResources>
  9. <Resource name="UserDatabase" auth="Container"
  10. type="org.apache.catalina.UserDatabase"
  11. description="User database that can be updated and saved"
  12. factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
  13. pathname="conf/tomcat-users.xml" />
  14. </GlobalNamingResources>
  15. <Service name="Catalina">
  16. <Connector port="8080" protocol="HTTP/1.1"
  17. connectionTimeout="20000"
  18. redirectPort="8443" />
  19. <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
  20. <Engine name="Catalina" defaultHost="localhost">
  21. <Realm className="org.apache.catalina.realm.LockOutRealm">
  22. <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
  23. resourceName="UserDatabase"/>
  24. </Realm>
  25. <Host name="localhost" appBase="webapps"
  26. unpackWARs="true" autoDeploy="true">
  27. <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
  28. prefix="localhost_access_log" suffix=".txt"
  29. pattern="%h %l %u %t &quot;%r&quot; %s %b" />
  30.  
  31. </Host>
  32. </Engine>
  33. </Service>
  34. </Server>
  这四种容器都符合一个协议:
  1. public interface Container extends Lifecycle {
  2. public void setName(String name);
  3. public Container getParent();
  4. public void setParent(Container container);
  5. public void addChild(Container child);
  6. public void removeChild(Container child);
  7. public Container findChild(String name);
  8. }
  我们可以看到该接口具有 getParent、setParent、addChild 和 removeChild 等方法,是一种透明组合模式。

版权声明:本文为nedulee原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/nedulee/p/12083709.html