• 合成(Composite)模式


      合成(composite)模式属于对象的结构模式,有时又叫部分-整体模式。合成模式将对象组织到数结构中,可以用来描述整体与部分的关系。

    文件系统

      一个文件系统就是一个典型的合成模式系统。下图所示就是常见的PC文件系统的一部分。

      文件系统是一个树结构,树上长有节点。树的节点有两种,一种是树枝节点,即目录,有内部树结构;一种是树叶节点,即文件。

      显然,可以把目录和文件当做同一种对象看待和处理,这就是合成模式的应用。

      合成模式可以不提供管理父对象的管理方法,但是合成模式必须在合适的地方管理子对象的管理方法。在什么地方声明子对象的管理方法,如add()、remove()、getChild()等方法。根据提供管理方法的位置不同分为两种:透明方式和安全方式。

    安全模式的合成模式

      此模式要求管理聚集的方法只出现在数枝构件类中,而不出现在树叶构件类中。其类图如下:

    涉及到3个角色:

    抽象构件(Component)角色:  这是一个抽象角色,它给参加组合的对象定义出公共的接口及其默认行为,可以用来管理所有的子对象。合成对象通常把它所包含的子对象当做类型为Component的对象。在安全式的合成模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝构件对象给出。

    树叶构件(Leaf)角色:树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为。

    树枝构件(Composite)角色:代表参加组合的有下级子对象的对象。树枝构件类给出所有的管理子对象的方法,如add()、remove()以及getChild()。

    代码如下:

    抽象构件角色

    package cn.qlq.composite;
    
    public interface Component {
    
        /**
         * 返回自身实例
         * 
         * @return
         */
        Component getComponent();
    
        /**
         * 打印组件信息
         */
        void printComponent();
    }

     叶子节点

    package cn.qlq.composite;
    
    public class Leaf implements Component {
    
        private String name;
    
        public Leaf(String name) {
            super();
            this.name = name;
        }
    
        @Override
        public Component getComponent() {
            return this;
        }
    
        @Override
        public void printComponent() {
            System.out.println(this);
        }
    
        @Override
        public String toString() {
            return "Leaf [name=" + name + "]";
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }

    数枝节点:

    package cn.qlq.composite;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Composite implements Component {
    
        private String name;
    
        private List<Component> components = new ArrayList<>();
    
        public Composite(String name) {
            super();
            this.name = name;
        }
    
        @Override
        public Component getComponent() {
            return this;
        }
    
        @Override
        public void printComponent() {
            System.out.println(this);
        }
    
        public void add(Component component) {
            if (!components.contains(component)) {
                components.add(component);
            }
        }
    
        public void remove(Component component) {
            if (components.contains(component)) {
                components.remove(component);
            }
        }
    
        public List<Component> getChilds() {
            return components;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public List<Component> getComponents() {
            return components;
        }
    
        public void setComponents(List<Component> components) {
            this.components = components;
        }
    
        @Override
        public String toString() {
            return "Composite [name=" + name + ", components=" + components + "]";
        }
    
    }

    测试代码:

    package cn.qlq.composite;
    
    public class Client {
    
        public static void main(String[] args) {
            Composite component = new Composite("component0");
            Composite component001 = new Composite("component001");
            Composite component002 = new Composite("component002");
    
            Component Leaf001 = new Leaf("leaf001");
            Component Leaf002 = new Leaf("leaf002");
    
            // 维护关系
            component001.add(Leaf001);
            component002.add(Leaf002);
    
            component.add(component001);
            component.add(component002);
    
            component.printComponent();
        }
    
    }

    结果:

    Composite [name=component0, components=[Composite [name=component001, components=[Leaf [name=leaf001]]], Composite [name=component002, components=[Leaf [name=leaf002]]]]]

     优缺点:

      优点是是一种安全的做法,因为树叶节点没有add()等方法,因此客户端对树叶节点调用这些方法时编译期间就不会通过。

      缺点是不够透明,因为树叶类和合成类将具有不同的接口,而且客户端调用相关方法需要区分树叶节点和数枝节点。

     

    透明模式的合成模式

       与安全模式不同的是,透明模式的合成模式要求所有的具体构件类,无论是叶子节点还是树叶节点,均符合一个固定的接口,也就是在所有节点都具备add()、remove()、getChild()等管理方法。只不过在叶子节点的不相干方法中不做任何操作。

    其类图如下:

     涉及到的角色:

    抽象构件(Component)角色:  这是一个抽象角色,它给参加组合的对象规定一个接口,规范共用的接口和默认行为。这个接口可以用来管理所有的子对象,要提供一个接口以规范取得和管理下层组件的接口,包括add、remove、getChild等管理方法。

    树叶构件(Leaf)角色:代表参加组合的树叶对象,定义出参加组合的原始对象的行为。树叶类会给出add、remove、getChild等方法的平庸实现(什么都不做,空方法)。

    树枝构件(Composite)角色:代表参加组合的有子对象的对象,定义出这样对象的行为。

    代码如下:

    package cn.qlq.composite;
    
    import java.util.List;
    
    public interface Component {
    
        /**
         * 返回自身实例
         * 
         * @return
         */
        Component getComponent();
    
        /**
         * 打印组件信息
         */
        void printComponent();
        
        void add(Component component);
        
        void remove(Component component);
        
        List<Component> getChilds();
    }
    package cn.qlq.composite;
    
    import java.util.List;
    
    public class Leaf implements Component {
    
        private String name;
    
        public Leaf(String name) {
            super();
            this.name = name;
        }
    
        @Override
        public Component getComponent() {
            return this;
        }
    
        @Override
        public void printComponent() {
            System.out.println(this);
        }
    
        @Override
        public String toString() {
            return "Leaf [name=" + name + "]";
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void add(Component component) {
            // ignored
        }
    
        @Override
        public void remove(Component component) {
            // ignored
        }
    
        @Override
        public List<Component> getChilds() {
            return null;
        }
    
    }
    package cn.qlq.composite;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Composite implements Component {
    
        private String name;
    
        private List<Component> components = new ArrayList<>();
    
        public Composite(String name) {
            super();
            this.name = name;
        }
    
        @Override
        public Component getComponent() {
            return this;
        }
    
        @Override
        public void printComponent() {
            System.out.println(this);
        }
    
        public void add(Component component) {
            if (!components.contains(component)) {
                components.add(component);
            }
        }
    
        public void remove(Component component) {
            if (components.contains(component)) {
                components.remove(component);
            }
        }
    
        public List<Component> getChilds() {
            return components;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public List<Component> getComponents() {
            return components;
        }
    
        public void setComponents(List<Component> components) {
            this.components = components;
        }
    
        @Override
        public String toString() {
            return "Composite [name=" + name + ", components=" + components + "]";
        }
    
    }

    客户端代码:主要变化是不再区分树叶和数枝,静态类型直接为接口,实际类型为数枝和树叶。

    package cn.qlq.composite;
    
    public class Client {
    
        public static void main(String[] args) {
            Component component = new Composite("component0");
            Component component001 = new Composite("component001");
            Component component002 = new Composite("component002");
    
            Component Leaf001 = new Leaf("leaf001");
            Component Leaf002 = new Leaf("leaf002");
    
            // 维护关系
            component001.add(Leaf001);
            component002.add(Leaf002);
    
            component.add(component001);
            component.add(component002);
    
            component.printComponent();
        }
    
    }

    结果:

    Composite [name=component0, components=[Composite [name=component001, components=[Leaf [name=leaf001]]], Composite [name=component002, components=[Leaf [name=leaf002]]]]]

    优缺点:

      这么做的好处是所有的构件类都有相同的接口,在客户端看来,树叶类对象与数枝对象的区别起码在接口层次上消失了。

      缺点是不安全,因为树叶节点和数枝节点在本质上是有区别的。树叶对象不可能有子节点的存在。因此调用树叶节点的add()、remove()、等方法容易引起运行时错误。

    两种模式的选择:

      安全性合成模式是指:从客户端使用合成模式上看是否更安全,如果是安全的,那么就不会有发生误操作的可能,能访问的方法都是被支持的。
      透明性合成模式是指:从客户端使用合成模式上,是否需要区分到底是“树枝对象”还是“树叶对象”。如果是透明的,那就不用区分,对于客户而言,都是Component对象,具体的类型对于客户端而言是透明的,是无须关心的。
      对于合成模式而言,在安全性和透明性上,会更看重透明性,毕竟合成模式的目的是:让客户端不再区分操作的是树枝对象还是树叶对象,而是以一个统一的方式来操作。

      而且对于安全性的实现,需要区分是树枝对象还是树叶对象。有时候,需要将对象进行类型转换,却发现类型信息丢失了,只好强行转换,这种类型转换必然是不够安全的。

      因此在使用合成模式的时候,建议多采用透明性的实现方式。

  • 相关阅读:
    input文本框加入xwebkitspeech实现语音输入功能
    获取textarea的光标位置
    初学者使用Application Cache指南
    一个封装了localStorage的增删改查的方法
    video from html5
    Asynchronous Method Invocation
    consume an asp.net webservice(upload a file to server) from java via soap
    INFO:VB/VBA (Long) 的转换自动化错误
    : 使用SAAJ发送和接收SOAP消息
    how to design a hardware service use .net remoting
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/11049493.html
Copyright © 2020-2023  润新知