Java设计模式-组合模式

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

解析

组合模式描述了如何将容器和叶子节点进行递归组合, 使用户在使用时可一致的对待容器和叶子, 为处理树形结构提供了完美的解决方案.

当容器对象的指定方法被调用时, 将遍历整个树形结构, 并执行调用. 整个过程递归处理.

(图片来源: 设计模式: 可复用面向对象软件的基础)

模式实现

案例: 杀毒软件

使对文件(Image/Text/Video/…)杀毒与对文件夹(Folder)的杀毒暴露统一接口.

Component

为组合模式中的对象声明接口, 在适当情况下, 实现所有类共有接口的默认行为.

声明一个接口用于访问和管理Component子组件.

/**
 * @author jifang
 * @since 16/8/24 上午10:19.
 */
public abstract class AbstractFileComponent {
    protected String name;
    protected AbstractFileComponent(String name) {
        this.name = name;
    }
    protected void printDepth(int depth) {
        for (int i = 0; i < depth; ++i) {
            System.out.print('-');
        }
    }
    protected abstract void add(AbstractFileComponent component);
    protected abstract void remove(AbstractFileComponent component);
    protected abstract void killVirus(int depth);
}

Leaf

叶子对象: 定义没有有分支节点的行为:

class ImageFileLeaf extends AbstractFileComponent {
    public ImageFileLeaf(String name) {
        super(name);
    }
    @Override
    public void add(AbstractFileComponent component) {
        throw new NotImplementedException(this.getClass() + " not implemented this method");
    }
    @Override
    public void remove(AbstractFileComponent component) {
        throw new NotImplementedException(this.getClass() + " not implemented this method");
    }
    @Override
    public void killVirus(int depth) {
        printDepth(depth);
        System.out.println("图片文件 [" + name + "]杀毒");
    }
}
class TextFileLeaf extends AbstractFileComponent {
    public TextFileLeaf(String name) {
        super(name);
    }
    @Override
    public void add(AbstractFileComponent component) {
        throw new NotImplementedException(this.getClass() + " not implemented this method");
    }
    @Override
    public void remove(AbstractFileComponent component) {
        throw new NotImplementedException(this.getClass() + " not implemented this method");
    }
    @Override
    public void killVirus(int depth) {
        printDepth(depth);
        System.out.println("文本文件 [" + name + "]杀毒");
    }
}
class VideoFileLeaf extends AbstractFileComponent {
    public VideoFileLeaf(String name) {
        super(name);
    }
    @Override
    public void add(AbstractFileComponent component) {
        throw new NotImplementedException(this.getClass() + " not implemented this method");
    }
    @Override
    public void remove(AbstractFileComponent component) {
        throw new NotImplementedException(this.getClass() + " not implemented this method");
    }
    @Override
    public void killVirus(int depth) {
        printDepth(depth);
        System.out.println("视频文件 [" + name + "]杀毒");
    }
}

Composite

容器对象: 定义有分支节点的行为, 用来存储子部件, 实现与子部件有关的操作:

public class FolderFileComposite extends AbstractFileComponent {
    private List<AbstractFileComponent> components = new LinkedList<>();
    public FolderFileComposite(String name) {
        super(name);
    }
    @Override
    public void add(AbstractFileComponent component) {
        components.add(component);
    }
    @Override
    public void remove(AbstractFileComponent component) {
        components.remove(component);
    }
    @Override
    public void killVirus(int depth) {
        printDepth(depth);
        System.out.println("目录 [" + name + "]杀毒");
        for (AbstractFileComponent component : components) {
            component.killVirus(depth + 2);
        }
    }
}

Client

public class Client {
    @Test
    public void client() {
        ImageFileLeaf image = new ImageFileLeaf("九寨沟.jpg");
        VideoFileLeaf video = new VideoFileLeaf("龙门飞甲.rmvb");
        TextFileLeaf text = new TextFileLeaf("解忧杂货店.txt");
        FolderFileComposite home = new FolderFileComposite("/home");
        home.add(image);
        home.add(video);
        home.add(text);
        FolderFileComposite root = new FolderFileComposite("/");
        root.add(home);
        root.add(new TextFileLeaf("/authorized_keys"));
        root.add(new FolderFileComposite("/etc"));
        root.killVirus(0);
    }
}

上面的实现方式是透明方式: 直接在Component中声明add()/remove(). 这样做的好处是叶节点和枝节点对于外界没有任何区别, 他们具有完全一致的行为接口. 但问题是对叶节点实现add()/remove()没有任何意义. 所以还有另一种实现方式安全方式, 也就是在Component中不去声明add()/remove(), 而是在Composite声明所有用来管理子类对象的方法, 不过由于不够透明, 所以叶节点与枝节点将不具有相同接口, 客户端调用需要作出相应判断, 带来了不便, 关于该问题的详细信息可参考: 组合模式(Composite)的安全模式与透明模式.

小结

组合模式定义了基本对象和组合对象的类层次结构, 基本对象可以被组合成更复杂的组合对象, 而这个组合对象又可以被组合, 这样不断地递归下去, 这样在客户代码中任何用到基本对象的地方都可以使用组合对象.

用户不用关心到底是处理一个叶节点还是处理一个枝节点, 也用不着为定义组合而写一些选择判断语句.

总的来说: 组合模式让用户可以一致地使用组合结构和单个对象.

场景

当需求中是体现部分与整体层次的结构时, 以及希望用户可以忽略组合对象与单个对象的不同, 统一地使用组合中的所有对象时, 就应该考虑使用组合模式了:

操作系统资源管理器

GUI容器视图

XML文件解析

OA系统中组织机构处理

Junit单元测试框架

TestCase(叶子)、TestUnite(容器)、Test接口(抽象)

本文分享自微信公众号 - Java帮帮(javahelp)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-01-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 第十一天 面向对象-接口多态【悟空教程】

    奋斗蒙
  • Java基础-day11-接口;多态案例练习

    Java基础-day11-接口&多态案例练习 题目要求1(多态): 定义家类 方法:饲养动物 动物类: 属性:年龄、姓名 方法:吃饭、睡觉 猫类、狗类、猪类均为...

    奋斗蒙
  • 24(02)多线程锁,线程通讯,线程组,线程池,多线程三种方式,匿名内部类,定时器,设计模式,单例模式,Runtime

    (6)多线程实现的第三种方案 package cn.itcast_09;(1) import java.util.concurrent.Callable; //...

    奋斗蒙
  • 23种设计模式详解(四)

    南风
  • 23种设计模式详解(四)

    顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

    南风
  • 基于邻接表的无向图的深度广度遍历实现

    张凝可
  • 常用设计模式——桥接模式

    在正式介绍桥接模式之前,我先跟大家谈谈两种常见文具的区别,它们是毛笔和蜡笔。假如我们需要大中小3种型号的画笔,能够绘制12种不同的颜色,如果使用蜡笔,需要准备3...

    用户5325874
  • Spring学习(2):Spring Bean管理(上)

    在前面使用了Spring的ApplicationContext,并通过它的getBean方法获取Spring的配置文件applicationContext.xm...

    编程思录
  • 新手学JAVA(六)----处理随机性的数据

    在我们的日常生活中会遇到很多随机性的事情,比如:摇奖,彩票,掷色子,这些都可以通过程序计算其中奖的概率。在JAVA的类库中,有一个专门操作这种随机性数据的类—-...

    令仔很忙
  • c#委托把方法当成参数

    跟着阿笨一起玩NET

扫码关注云+社区

领取腾讯云代金券