Java设计模式(十七)—— 组合模式
Posted 小小印z
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java设计模式(十七)—— 组合模式相关的知识,希望对你有一定的参考价值。
组合模式的定义如下:将对象组合成树形结构以表示“部分-整体”的层次结构,让用户对单个对象和组合对象的使用具有一致性。
适用组合模式的情景如下:
- 希望表示对象的“部分—整体”层次结构
- 希望用户用一致方式处理个体和组合对象
一、问题的提出
我们研究的问题有许多树形结构的问题,例如文件结构:
例如,要用程序创建文件结构,为了验证正确与否,还要再控制台上输出从某目录开始的所有文件信息。文件树形结构可以分为两类,一类是文件叶子节点,无后继节点,一类是中间目录节点,有后继节点。具体代码如下:
(1)文件节点类
public class FileLeaf
String fileName;
public FileLeaf(String fileName)
this.fileName = fileName;
public void display()
System.out.println(fileName);
(2)中间目录节点类 DirectNode
public class DirectNode
String nodeName;
public DirectNode(String nodeName)
this.nodeName = nodeName;
//后继目录集合
ArrayList<DirectNode> nodeList = new ArrayList<>();
//当前目录文件集合
ArrayList<FileLeaf> fileList = new ArrayList<>();
//添加下一级子目录
public void addNode(DirectNode node )
nodeList.add(node);
//添加本级文件
public void addLeaf(FileLeaf leaf)
fileList.add(leaf);
//从本级目录开始显示
public void display()
for (int i = 0; i < fileList.size(); i++)
fileList.get(i).display();
for (int i = 0; i < nodeList.size(); i++)
System.out.println(nodeList.get(i).nodeName);
nodeList.get(i).display();
(3)测试类
public class Test
public static void createTree(DirectNode node)
File f = new File(node.nodeName);
File f2[] = f.listFiles();
for (int i = 0; i < f2.length; i++)
//如果是文件类型,则把他添加到当前目录文件集合
if (f2[i].isFile())
FileLeaf l = new FileLeaf(f2[i].getAbsolutePath());
node.addLeaf(l);
//如果是目录类型,则把他添加到目录集合,然后继续递归添加
if (f2[i].isDirectory())
DirectNode node2 = new DirectNode(f2[i].getAbsolutePath());
node.addNode(node2);
createTree(node2);
public static void main(String[] args)
DirectNode start = new DirectNode("D:\\\\学习笔记\\\\Linux\\\\docker-book-master\\\\docker");
createTree(start);
start.display();
二、组合模式
从上面图片可知:根目录是由两个子目录组成的;第一个子目录由两个文件组成;第二个子目录也由两个文件组成,因此树形形式也可以叫做组合模式。
在图中,把节点分为叶子节点与目录节点,它们是孤立的。然后把叶子节点与目录节点都看成相同性质的节点,只不过目录节点的后继节点不为空,而叶子节点的后继节点为null。这样就能够对树形结构的所有节点执行相同的操作,这也是组合模式最大的特点。
采用组合模式修改上面例子的功能:
(1)定义抽象节点类Node
该类是叶子节点与目录节点的父类,节点名称是name。其主要包括两类方法:一类方法是所有节点具有相同形式、不同内容的方法。这类方法要定义成抽象方法,如display();另一类方法是目录节点必须重写,而叶子节点不需要重写的方法,相当于为叶子节点提供了默认实现,如addNode()方法。因为叶子对象没有该功能,所以可以通过抛出异常防止叶子节点无效调用该方法。/
public abstract class Node
protected String name;
public Node(String name)
this.name = name;
public void addNode(Node node) throws Exception
throw new Exception("无效的异常");
abstract void display();
(2)文件叶子节点类 FileNode
public class FileNode extends Node
public FileNode(String name)
super(name);
@Override
void display()
System.out.println(name);
(3)目录节点类 DirectNode
该类从Node抽象类派生后,与原DirectNode类相比,主要有以下不同:
- 由定义两个结合类成员变量转为定义一个集合类成员变量nodeList
- 由定义两个添加方法转为定义一个添加方法addNode()
- display() 方法中,由两个不同元素的循环转为一个对相同性质节点Node循环。
public class DirectNode2 extends Node
private ArrayList<Node> nodeList = new ArrayList<>();
public DirectNode2(String name)
super(name);
public void addNode(Node node) throws Exception
nodeList.add(node);
@Override
void display()
System.out.println(name);
for (int i = 0; i < nodeList.size(); i++)
nodeList.get(i).display();
(4) 测试类
public class Test2
public static void createTree(Node node) throws Exception
File f = new File(node.name);
File f2[] = f.listFiles();
for (int i = 0; i < f2.length; i++)
if (f2[i].isFile())
Node node2 = new FileNode(f2[i].getAbsolutePath());
node.addNode(node2);
if (f2[i].isDirectory())
Node node2 = new DirectNode2(f2[i].getAbsolutePath());
node.addNode(node2);
createTree(node2);
public static void main(String[] args) throws Exception
Node start = new DirectNode2("D:\\\\学习笔记\\\\Linux\\\\docker-book-master\\\\docker");
createTree(start);
start.display();
[设计模式] 设计模式课程(十七)--组合模式
概述
- 数据结构模式
- 常常有一些组件在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大地破坏组件的复用
- 将这些特定数据模式封装在内部,对外提供统一的接口,来实现与特定数据结构无关的访问
- 典型模式:Composite, Iterator, Chain of Resposibility
- 属于结构模式(按目的划分)
- 动机:软件在某些情况下,客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端
- 如何将“客户代码与复杂的对象容器结构”解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?
- 定义:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)
- 采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化成“一对一”的关系,使得客户代码可以一致地(复用)处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器
- 将“客户代码与复杂的对象容器结构”结构是Composite的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖,从而更能“应对变化”
- Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率
示例
- 34-36:多态调用,将内部数据结构封装
1 #include <iostream> 2 #include <list> 3 #include <string> 4 #include <algorithm> 5 6 using namespace std; 7 8 class Component 9 { 10 public: 11 virtual void process() = 0; 12 virtual ~Component(){} 13 }; 14 15 //树节点 16 class Composite : public Component{ 17 18 string name; 19 list<Component*> elements; 20 public: 21 Composite(const string & s) : name(s) {} 22 23 void add(Component* element) { 24 elements.push_back(element); 25 } 26 void remove(Component* element){ 27 elements.remove(element); 28 } 29 30 void process(){ 31 32 //1. process current node 33 34 //2. process leaf nodes 35 for (auto &e : elements) 36 e->process(); //多态调用 37 38 } 39 }; 40 41 //叶子节点 42 class Leaf : public Component{ 43 string name; 44 public: 45 Leaf(string s) : name(s) {} 46 47 void process(){ 48 //process current node 49 } 50 }; 51 52 void Invoke(Component & c){ 53 //... 54 c.process(); 55 //... 56 } 57 58 int main() 59 { 60 61 Composite root("root"); 62 Composite treeNode1("treeNode1"); 63 Composite treeNode2("treeNode2"); 64 Composite treeNode3("treeNode3"); 65 Composite treeNode4("treeNode4"); 66 Leaf leat1("left1"); 67 Leaf leat2("left2"); 68 69 root.add(&treeNode1); 70 treeNode1.add(&treeNode2); 71 treeNode2.add(&leaf1); 72 73 root.add(&treeNode3); 74 treeNode3.add(&treeNode4); 75 treeNode4.add(&leaf2); 76 77 process(root); 78 process(leaf2); 79 process(treeNode3); 80 81 }
总结
以上是关于Java设计模式(十七)—— 组合模式的主要内容,如果未能解决你的问题,请参考以下文章