说明:本文介绍结构型设计模式之一的组合模式
定义
组合模式(Composite Pattern)又叫作整体-部分(Part-Whole)模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性,属于结构型设计模式。(引自《设计模式就该这样学》P263)
文件系统
以文件系统为例,如下,是服务器上某个文件夹的文件结构,该文件夹下既有文件夹,也有文件
如果我要构建这样一个文件夹-文件系统,代码应该是这样写的,如下:
(图片文件,ImageFile)
/**
* 图片文件
*/
public class ImageFile {
/**
* 图片名称
*/
private String name;
public ImageFile(String name) {
this.name = name;
}
/**
* 展示
*/
public void show(int space) {
for (int i = 0; i < space; i++) {
System.out.print(" ");
}
System.out.println(name);
}
}
(电影文件,MovieFile)
/**
* 电影文件
*/
public class MovieFile {
/**
* 电影名称
*/
private String name;
public MovieFile(String name) {
this.name = name;
}
/**
* 展示
*/
public void show(int space) {
for (int i = 0; i < space; i++) {
System.out.print(" ");
}
System.out.println(name);
}
}
(文件夹,Folder,定义多个文件集合,并开放对应的增加方法)
import java.util.ArrayList;
/**
* 文件夹
*/
public class Folder {
/**
* 文件夹名称
*/
private String name;
/**
* 文件夹下的文件夹
*/
private ArrayList<Folder> folders = new ArrayList<>();
/**
* 文件夹下的图片文件
*/
private ArrayList<ImageFile> imageFiles = new ArrayList<>();
/**
* 文件夹下的视频文件
*/
private ArrayList<MovieFile> movieFiles = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
/**
* 添加文件夹
*/
public void addFolder(Folder folder) {
folders.add(folder);
}
/**
* 添加图片文件
*/
public void addImageFile(ImageFile imageFile) {
imageFiles.add(imageFile);
}
/**
* 添加电影文件
*/
public void addMoveFile(MovieFile movieFile) {
movieFiles.add(movieFile);
}
/**
* 展示
*/
public void show(int space) {
// 打印文件夹
space++;
for (int i = 0; i < space; i++) {
System.out.print(" ");
}
System.out.println(name);
for (Folder folder : folders) {
folder.show(space);
}
// 打印图片文件
space++;
for (ImageFile imageFile : imageFiles) {
imageFile.show(space);
}
// 打印电影文件
space++;
for (MovieFile movieFile : movieFiles) {
movieFile.show(space);
}
}
}
(客户端使用,Client)
public class Client {
public static void main(String[] args) {
// 顶级文件夹
Folder folder = new Folder("folder");
// 二级文件夹
Folder images = new Folder("images");
Folder movies = new Folder("movies");
// 三级目录下的文件
ImageFile boy = new ImageFile("boy.png");
ImageFile girl = new ImageFile("girl.png");
images.addImageFile(boy);
images.addImageFile(girl);
// 三级文件夹
Folder director1 = new Folder("heizeming");
Folder director2 = new Folder("xiaolinzhengshu");
// 四级目录
MovieFile movieFile1 = new MovieFile("luoshengmen.mp4");
MovieFile movieFile2 = new MovieFile("qiwushi.mp4");
director1.addMoveFile(movieFile1);
director1.addMoveFile(movieFile2);
MovieFile movieFile3 = new MovieFile("duomingjian.mp4");
MovieFile movieFile4 = new MovieFile("qiefu.mp4");
director2.addMoveFile(movieFile3);
director2.addMoveFile(movieFile4);
movies.addFolder(director1);
movies.addFolder(director2);
// 文件夹添加到顶级文件夹
folder.addFolder(images);
folder.addFolder(movies);
folder.show(0);
}
}
运行,hora!(看!),能实现目的
但这里存在问题,文件系统,简单来说只有文件夹和文件两个实体对象,代码中的图片文件、电影文件可以抽象为文件(File),这是整体与个体的场景。
组合模式
使用组合模式改进上述代码,如下:
(抽象节点类,Node)
/**
* 抽象节点
*/
public abstract class Node {
/**
* 节点名称
*/
protected String name;
public Node(String name) {
this.name = name;
}
/**
* 添加节点
*/
protected abstract void add(Node node);
/**
* 展示
*/
protected void show(int space) {
for (int i = 0; i < space; i++) {
System.out.print(" ");
}
System.out.println(name);
}
/**
* 重载方法,使用的时候就不用给参数了
*/
protected void show() {
show(0);
}
}
(文件夹,Folder)
import java.util.ArrayList;
/**
* 文件夹
*/
public class Folder extends Node {
/**
* 文件夹下的子节点
*/
private ArrayList<Node> childrenNodes = new ArrayList<>();
/**
* 调用父类的构造方法
*/
public Folder(String name) {
super(name);
}
@Override
protected void add(Node node) {
childrenNodes.add(node);
}
@Override
protected void show(int space) {
super.show(space);
space++;
for (Node node : childrenNodes) {
node.show(space);
}
}
}
(文件,File)
/**
* 文件
*/
public class File extends Node {
/**
* 调用父类的构造方法
*/
public File(String name) {
super(name);
}
@Override
protected void add(Node node) {
System.out.println("不能添加子节点");
}
@Override
protected void show(int space) {
super.show(space);
}
}
(客户端使用,Client)
public class Client {
public static void main(String[] args) {
// 顶级文件夹
Folder folder = new Folder("folder");
// 二级文件夹
Folder images = new Folder("images");
Folder movies = new Folder("movies");
// 三级目录下的文件
File boy = new File("boy.png");
File girl = new File("girl.png");
images.add(boy);
images.add(girl);
// 三级文件夹
Folder director1 = new Folder("heizeming");
Folder director2 = new Folder("xiaolinzhengshu");
// 四级目录
File movieFile1 = new File("luoshengmen.mp4");
File movieFile2 = new File("qiwushi.mp4");
director1.add(movieFile1);
director1.add(movieFile2);
File movieFile3 = new File("duomingjian.mp4");
File movieFile4 = new File("qiefu.mp4");
director2.add(movieFile3);
director2.add(movieFile4);
movies.add(director1);
movies.add(director2);
// 文件夹添加到顶级文件夹
folder.add(images);
folder.add(movies);
folder.show();
}
}
执行如下,也实现了目的
这么看下来,文件系统场景使用组合模式实现是很不错的,代码少了很多,也削减了文件夹类中的职责(可以对比下Folder类前后的代码)
使用场景
在《设计模式就该这样学》(P229)这本书中,提到状态模式适用于以下场景:
(1)希望客户端可以忽略组合对象与单个对象的差异;
(2)对象层次具备整体和部分,呈树形结构;
除了文件系统、企业组织架构场景,我还没想到其他使用场景;
总结
本文介绍了结构型设计模式中的组合模式,参考《设计模式就该这样学》、《秒懂设计模式》、《设计模式的艺术》(第一版)这三本书,其中的例子来自《秒懂设计模式》。