《Android源码设计模式》之组合模式

组合模式介绍

组合模式(Composite Pattern)也称为部分整体模式(Part-Whole-Pattern),结构型设计模式之一,它将一组相似的对象看作一个对象处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象集合之间的差别。

组合模式的定义

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

组合模式的使用场景

表示对象的部分-整体层次结构时。
  从一个整体中能够独立出部分模块或功能的场景。

组合模式的UML类图

在这里插入图片描述
根据类图我们可以得出如下一个组合模式的通用模式代码。

抽象根节点
package com.guifa.patterndemo.compositepattern;

public abstract class Component {
    // 节点名
    protected String name;

    public Component(String name) {
        this.name = name;
    }

    // 具体的逻辑方法由子类实现
    public abstract void doSomething();
}
具体枝干节点
package com.guifa.patterndemo.compositepattern;

import java.util.ArrayList;
import java.util.List;

public class Composite extends Component {

    private List<Component> components = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void doSomething() {
        System.out.println(name);
        if (null != components) {
            for (Component c : components) {
                c.doSomething();
            }
        }
    }

    /**
     * 添加子节点
     *
     * @param child 子节点
     */
    public void addChild(Component child) {
        components.add(child);
    }

    /**
     * 移除子节点
     *
     * @param child 子节点
     */
    public void removeChild(Component child) {
        components.remove(child);
    }

    /**
     * 获取子节点
     *
     * @param index 子节点对应下标
     * 
     * @return 子节点
     */
    public Component getChildren(int index) {
        return components.get(index);
    }
}
具体叶子节点
package com.guifa.patterndemo.compositepattern;

public class Leaf extends Component {

    public Leaf(String name) {
        super(name);
    }

    @Override
    public void doSomething() {
        System.out.println(name);
    }
}
客户类
package com.guifa.patterndemo.compositepattern;

public class Client {
    public static void main(String[] args) {
        // 构造一个根节点
        Composite root = new Composite("Root");
        // 构造两个枝干节点
        Composite branch1 = new Composite("Branch1");
        Composite branch2 = new Composite("Branch2");
        // 构造两个叶子节点
        Leaf leaf1 = new Leaf("Leaf1");
        Leaf leaf2 = new Leaf("Leaf2");
        // 将叶子节点添加至枝干节点中
        branch1.addChild(leaf1);
        branch2.addChild(leaf2);
        // 将枝干节点添加至根节点中
        root.addChild(branch1);
        root.addChild(branch2);
        // 执行方法
        root.doSomething();
    }
}

输出结果:

Root
Branch1
Leaf1
Branch2
Leaf2

Process finished with exit code 0

角色介绍:

  • Component:抽象根节点,为组合中的对象声明接口。在适当的情况下,实现所有类共有接口的缺省行为。声明一个接口用于访问和管理Component的子节点。可以在递归结构中定义一个接口,用于访问一个父节点,并在合适的情况下实现它。
  • Composite:定义子节点的那些枝干节点的行为,存储子节点,在Component接口中实现与子节点有关的操作。
  • Leaf:在组合中表示叶子节点对象,叶子节点没有子节点,在组合中定义节点对象的行为。
  • Client:通过Component接口操纵组合节点对象。

上述例子中我们在Client客户类中直接使用了Component的实现类,违反了依赖倒置原则。我们稍微修改下上面的类图将位于Composite中的一些实现方法定义到Component中,那么我们会得到一个不一样的组合模式,如图:
在这里插入图片描述
像这样将组合所使用的方法定义在抽象类的方式称为透明的组合模式,而上面我们所说的组合模式则称为安全的组合模式。透明组合模式中不管是叶子节点还是枝干节点都有着相同的结构,那意味着我们无法通过geChildren方法得到子节点的类型,而必须在方法实现的内部进行判断,代码如下:

透明的组合模式抽象根节点
package com.guifa.patterndemo.compositepattern;

public abstract class Component {
    // 节点名
    protected String name;

    public Component(String name) {
        this.name = name;
    }

    // 具体的逻辑方法由子类实现
    public abstract void doSomething();

    /**
     * 添加子节点
     *
     * @param child 子节点
     */
    public abstract void addChild(Component child);

    /**
     * 移除子节点
     *
     * @param child 子节点
     */
    public abstract void removeChild(Component child);

    /**
     * 获取子节点
     *
     * @param index 子节点对应下标
     * @return 子节点
     */
    public abstract Component getChildren(int index);
}
透明的组合模式具体枝干节点
package com.guifa.patterndemo.compositepattern;

import java.util.ArrayList;
import java.util.List;

public class Composite extends Component {

    /**
     * 存储节点的容器
     */
    private List<Component> components = new ArrayList<>();

    public Composite(String name) {
        super(name);
    }

    @Override
    public void doSomething() {
        System.out.println(name);
        if (null != components) {
            for (Component c : components) {
                c.doSomething();
            }
        }
    }

    /**
     * 添加子节点
     *
     * @param child 子节点
     */
    public void addChild(Component child) {
        components.add(child);
    }

    /**
     * 移除子节点
     *
     * @param child 子节点
     */
    public void removeChild(Component child) {
        components.remove(child);
    }

    /**
     * 获取子节点
     *
     * @param index 子节点
     */
    public Component getChildren(int index) {
        return components.get(index);
    }
}
透明的组合模式叶子节点
package com.guifa.patterndemo.compositepattern;

public class Leaf extends Component {

    public Leaf(String name) {
        super(name);
    }

    @Override
    public void doSomething() {
        System.out.println(name);
    }

    @Override
    public void addChild(Component child) {
        throw new UnsupportedOperationException("叶子节点没有子节点");
    }

    @Override
    public void removeChild(Component child) {
        throw new UnsupportedOperationException("叶子节点没有子节点");
    }

    @Override
    public Component getChildren(int index) {
        throw new UnsupportedOperationException("叶子节点没有子节点");
    }
}
客户类
package com.guifa.patterndemo.compositepattern;

public class Client {
    public static void main(String[] args) {
        // 构造一个根节点
        Composite root = new Composite("Root");
        // 构造两个枝干节点
        Composite branch1 = new Composite("Branch1");
        Composite branch2 = new Composite("Branch2");
        // 构造两个叶子节点
        Leaf leaf1 = new Leaf("Leaf1");
        Leaf leaf2 = new Leaf("Leaf2");
        // 将叶子节点添加至枝干节点中
        branch1.addChild(leaf1);
        branch2.addChild(leaf2);
        // 将枝干节点添加至根节点中
        root.addChild(branch1);
        root.addChild(branch2);
        // 执行方法
        root.doSomething();
    }
}

组合模式的简单实现

我们以电脑上的文件系统为例,看看一个简单的文件系统是如何构成的,首先声明一个Dir抽象类表示文件和文件夹。

表示文件和文件夹的抽象类
package com.guifa.patterndemo.compositepattern;

import java.util.ArrayList;
import java.util.List;

public abstract class Dir {

    protected List<Dir> dirs = new ArrayList<>();
    // 当前文件或文件夹名
    private String name;

    public Dir(String name) {
        this.name = name;
    }

    /**
     * 添加一个文件或文件夹
     *
     * @param dir 文件或文件夹
     */
    public abstract void addDir(Dir dir);

    /**
     * 移除一个文件或文件夹
     *
     * @param dir 文件或文件夹
     */
    public abstract void rmDir(Dir dir);

    /**
     * 清空文件夹下所有元素
     */
    public abstract void clear();

    /**
     * 输出文件夹目录结构
     */
    public abstract void print();

    /**
     * 获取文件夹下所有的文件或文件夹
     *
     * @return 文件夹下所有的文件或子文件夹
     */
    public abstract List<Dir> getFiles();

    /**
     * 获取文件或文件夹名
     *
     * @return 文件或文件夹名
     */
    public String getName() {
        return name;
    }
}

在该抽象类中我没定义了相关的抽象方法,大家可以看到这里用到的就是所谓的透明的组合模式。

表示文件夹的类
package com.guifa.patterndemo.compositepattern;

import java.util.Iterator;
import java.util.List;

public class Folder extends Dir {

    public Folder(String name) {
        super(name);
    }

    @Override
    public void addDir(Dir dir) {
        dirs.add(dir);
    }

    @Override
    public void rmDir(Dir dir) {
        dirs.remove(dir);
    }

    @Override
    public void clear() {
        dirs.clear();
    }

    @Override
    public void print() {
        System.out.print(getName() + "(");
        Iterator<Dir> iterator = dirs.iterator();
        while (iterator.hasNext()) {
            Dir dir = iterator.next();
            dir.print();
            if (iterator.hasNext()) {
                System.out.print(",");
            }
        }
        System.out.print(")");
    }

    @Override
    public List<Dir> getFiles() {
        return dirs;
    }
}

表示文件的类
package com.guifa.patterndemo.compositepattern;

import java.util.List;

public class File extends Dir {

    public File(String name) {
        super(name);
    }

    @Override
    public void addDir(Dir dir) {
        throw new UnsupportedOperationException("文件对象不支持该操作");
    }

    @Override
    public void rmDir(Dir dir) {
        throw new UnsupportedOperationException("文件对象不支持该操作");
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException("文件对象不支持该操作");
    }

    @Override
    public void print() {
        System.out.print(getName());
    }

    @Override
    public List<Dir> getFiles() {
        throw new UnsupportedOperationException("文件对象不支持该操作");
    }
}

客户类
package com.guifa.patterndemo.compositepattern;

public class Client {
    public static void main(String[] args) {
        // 构造一个目录对象表示C盘根目录
        Dir diskC = new Folder("C");
        // C盘根目录下有一个文件ImbaMallLog.txt
        diskC.addDir(new File("ImbaMallLog.txt"));
        // C盘根目录下还有3个子目录Windows、PerfLogs、Program File
        Dir dirWin = new Folder("Windows");
        // Windows目录下有文件explorer.exe
        dirWin.addDir(new File("explorer.exe"));
        diskC.addDir(dirWin);
        // PerfLogs目录
        Dir dirPer = new Folder("PerfLogs");
        // PerfLogs目录下有文件null.txt
        dirPer.addDir(new File("null.txt"));
        diskC.addDir(dirPer);
        // Program File目录
        Dir dirPro = new Folder("Program File");
        // Program File目录下有文件ftp.txt
        dirPro.addDir(new File("ftp.txt"));
        diskC.addDir(dirPro);
        // 打印出文件结构
        diskC.print();
    }
}

输出结果:

C(ImbaMallLog.txt,Windows(explorer.exe),PerfLogs(null.txt),Program File(ftp.txt))
Process finished with exit code 0

这里我们以括号作为一个文件夹的内容范围,如上输出所示,C盘文件夹下有3个子文件夹Windows、PerfLogs和Program File以及一个文件ImbaMallLog.txt,而且3个子文件夹中还各自包含有子文件,一个典型的树状嵌套结构,这就是组合模式。

总结

组合模式的优点:
组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让高层模块忽略了层次的差异,方便对整个层次结构进行控制。
高层模块可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了高层模块的代码。
在组合模式中增加新的枝干构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和枝干对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

组合模式的缺点:
在新增构件时不好对枝干中的构件类型进行限制,不能依赖类型系统来施加这些约束,因为在大多数情况下,它们都来自于相同的抽象层,此时,必须进行类型检查来实现,这个实现过程较为复杂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android源码设计模式解析与实践是一本关于Android系统中的设计模式的书籍,旨在通过解析Android源码中的实际案例来理解和应用设计模式Android系统是一个庞大而复杂的开源项目,其中包含了大量的设计模式。这些设计模式不仅帮助Android系统实现了高效、稳定、易于扩展的特性,也可以为Android开发者提供参考和借鉴的经验。 本书首先介绍了设计模式的概念和基本原理,包括单例模式、工厂模式、观察者模式、策略模式等。然后,结合Android源码中的具体实例,详细讲解了这些设计模式Android系统中的应用场景和实践方法。 例如,书中通过分析Android系统中的Activity、Fragment、View等核心组件的源码,解析了它们是如何应用观察者模式和状态模式来实现界面更新和事件传递的。又如,书中通过分析Android系统中的Handler、Looper、MessageQueue等核心类的源码,讲解了它们是如何应用责任链模式来实现线程间通信和消息处理的。 此外,本书还探讨了Android系统中的一些特殊设计模式,如MVC模式、MVP模式、MVVM模式等,帮助读者理解和应用这些模式来构建更加优雅和可维护的Android应用程序。 总之,通过学习和实践本书中介绍的Android源码设计模式,读者可以更深入地了解Android系统的设计原则和实践经验,提升自己的Android开发技能,并能够更加高效地开发出高质量的Android应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值