zoukankan      html  css  js  c++  java
  • 趣谈设计模式——组合模式

    组合模式的定义

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

    案例

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

    这样做有什么好处?

    首先整体是由部分组成的, 每个部分其实可以作为其部分的整体,递归下去,所以我们代码中调用部分的地方,都可以用整体来替换。
    (比如CTO体系/企业应用研发部/企业信息化部,CTO体系是一个整体,企业应用研发部是部分,但是对于企业信息化部来说,企业应用研发部其实也是一个整体)
    其次如果整体和部分具有一致性,那么对于我们来说,将不用去书写代码去区分不同的部分了,而是可以以一种统一的方式去处理。

    实现模型:

    组合模式在代码具体实现上,有两种不同的方式:
    1.透明组合模式:把组合(树节点)使用的方法放到统一行为(Component)中,让不同层次(树节点,叶子节点)的结构都具备一致行为;
    2.安全组合模式:统一行为(Component)只规定系统各个层次的最基础的一致行为,而把组合(树节点)本身的方法(管理子类对象的添加,删除等)放到自身当中;

    为什么会有两种实现模型呢?

    首先就是透明模式,会引入不必要的依赖,比如叶子结点其实是不需备add和remove功能的。所以才有了安全模式,将节点本身的方法放到自身维护,这样做的缺点就是各个节点间是有差异的,没有办法用完全统一的方式去处理。虽然实现不同,但是它们都遵循组合模式的规则。

    对于两种模式的思考

    其实组合模式是一种反范式的设计模式,比如透明模式会引入不必要依赖的行为,违反了接口隔离原则, 安全模式区分了叶子结点和树枝节点,导致客户端没有办法依赖抽象,违反了依赖倒置原则,基类也不能用子类去替换,违反了里式替换原则。

    代码实现

    接下来我们来实现透明模式组合模式的代码:
    /**
     * 组合模式协议.
     *
     * @author jialin.li
     * @date 2019-12-20 15:22
     */
    public abstract class AbstractComponent {
        String name;
    
        public AbstractComponent(String name) {
            this.name = name;
        }
    
        public abstract void add(AbstractComponent c);
        public abstract void remove(AbstractComponent c);
        public abstract void display(int depth);
    }
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 组合模式中的枝叶,用来存储子部件.
     *
     * @author jialin.li
     * @date 2019-12-20 15:28
     */
    public class Composite extends AbstractComponent {
        private List<AbstractComponent> children = new ArrayList<>();
    
        public Composite(String name) {
            super(name);
        }
    
        @Override
        public void add(AbstractComponent c) {
            children.add(c);
        }
    
        @Override
        public void remove(AbstractComponent c) {
            children.remove(c);
        }
    
        @Override
        public void display(int depth) {
            for (int i = 0; i < depth; i++) {
                System.out.print("-");
            }
            System.out.println(name);
            for (AbstractComponent component : children) {
                component.display(depth + 1);
            }
        }
    }
    /**
     * 组合模式中的叶子节点.
     *
     * @author jialin.li
     * @date 2019-12-20 15:26
     */
    public class Leaf extends AbstractComponent {
    
        public Leaf(String name) {
            super(name);
        }
    
        @Override
        public void add(AbstractComponent c) {
            System.out.println("Cannot add a leaf!");
        }
    
        @Override
        public void remove(AbstractComponent c) {
            System.out.println("Cannot remove a leaf!");
        }
    
        @Override
        public void display(int depth) {
            for (int i = 0; i < depth; i++) {
                System.out.print("-");
            }
            System.out.println(name);
        }
    }
    /**
     * 客户端.
     *
     * @author jialin.li
     * @date 2019-12-20 15:33
     */
    public class Main {
        public static void main(String[] args) {
            Composite root = new Composite("root");
            root.add(new Leaf("Leaf A"));
            root.add(new Leaf("Leaf B"));
    
            Composite comp = new Composite("Composite X");
            comp.add(new Leaf("Leaf XA"));
            comp.add(new Leaf("Leaf XB"));
    
            root.add(comp);
    
            Composite comp2 = new Composite("Composite XY");
            comp2.add(new Leaf("Leaf XYA"));
            comp2.add(new Leaf("Leaf XYB"));
            comp.add(comp2);
    
            root.add(new Leaf("Leaf C"));
            Leaf d = new Leaf("D");
            root.add(d);
            root.remove(d);
    
            root.display(1);
        }
    }

    执行结果

    -root
    --Leaf A
    --Leaf B
    --Composite X
    ---Leaf XA
    ---Leaf XB
    ---Composite XY
    ----Leaf XYA
    ----Leaf XYB
    --Leaf C

    Tomcat中的组合模式

    tomcat中的容器设计,也用到了组合模式:
    tomcat中设计了4中容器:Engine、Host、Context、Wrapper。这四种容器的关系如下:
    它们之间是父子关系,除了Engine容器,其他都可以具有多个。容器是一个树状的层级结构
    engine表示引擎,一个Service最多只能有一个引擎,Host表示虚拟主机、Context表示一个Web应用、Wrapper表示一个Servlet(后三种容器可以有多个)
    Tomcat 的server.xml配置文件中的标签,其实就代表了tomcat的层级结构:
    <?xml version="1.0" encoding="UTF-8"?>
    <Server port="8005" shutdown="SHUTDOWN">
      <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
      <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
      <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
      <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
      <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
      <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml" />
      </GlobalNamingResources>
      <Service name="Catalina">
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
        <Engine name="Catalina" defaultHost="localhost">
          <Realm className="org.apache.catalina.realm.LockOutRealm">
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                   resourceName="UserDatabase"/>
          </Realm>
          <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true">
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                   prefix="localhost_access_log" suffix=".txt"
                   pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    
          </Host>
        </Engine>
      </Service>
    </Server>
    这四种容器都符合一个协议:
    public interface Container extends Lifecycle {
        public void setName(String name);
        public Container getParent();
        public void setParent(Container container);
        public void addChild(Container child);
        public void removeChild(Container child);
        public Container findChild(String name);
    }
    我们可以看到该接口具有 getParent、setParent、addChild 和 removeChild 等方法,是一种透明组合模式。

  • 相关阅读:
    Codeforces Round #370 (Div. 2) C. Memory and De-Evolution 水题
    Codeforces Round #370 (Div. 2) B. Memory and Trident 水题
    Codeforces Round #370 (Div. 2) A. Memory and Crow 水题
    Codeforces Round #258 (Div. 2) E. Devu and Flowers 容斥
    Codeforces Round #258 (Div. 2) D. Count Good Substrings 水题
    Codeforces Round #258 (Div. 2) C. Predict Outcome of the Game 水题
    Codeforces Round #258 (Div. 2) . Sort the Array 贪心
    Codeforces Round #258 (Div. 2) A. Game With Sticks 水题
    Codeforces Beta Round #14 (Div. 2) D. Two Paths 树形dp
    Codeforces Beta Round #14 (Div. 2) C. Four Segments 水题
  • 原文地址:https://www.cnblogs.com/nedulee/p/12083709.html
Copyright © 2011-2022 走看看