请看下面的完整攻略:
设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解
什么是组合模式
组合模式(Composite Pattern),也叫部分-整体模式,是一种结构型设计模式。通过将对象组合成树形结构,以表示“整体-部分”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,即将对象的组合与单个对象的使用同等对待。
组合模式由以下角色构成:
- Component(抽象构件):是组合中对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
- Leaf(叶子构件):在组合中表示叶子节点对象,叶子节点没有子节点。
- Composite(容器构件):表示容器组件,容器组件包含一些子节点,用来定义有枝节点的行为,实现在Component接口中的有关操作,如增加(add)和删除(remove)子节点等。
组合模式的优缺点
组合模式的优点在于:
- 定义了包含基本对象和组合对象的类层次结构。
- 简化了客户端代码,使得客户端可以像处理单个对象一样来处理组合对象。
- 更容易增加新的组合对象和叶子对象。
组合模式的缺点在于:
- 可能会增加系统的复杂性。
- 实现组合模式需要遵循一定的规则和约束,否则可能会破坏整个模式结构。
组合模式在JDK中的运用
在JDK中,使用组合模式的典型例子就是针对集合类框架中的容器和叶子节点的设计。
以ArrayList为例,其源码如下:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
// Object array buffer for ArrayList
private transient Object[] elementData;
// ...
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// ...
}
在ArrayList中,添加元素的方法add就是一个容器对象,而ArrayList中存储的元素则是叶子节点对象。ArrayList内部维护了一个Object数组作为容器,当添加元素时,向该数组中增加元素作为叶子节点。
组合模式在MyBatis中的运用
在MyBatis中,组合模式被应用到SQL语句的构建和执行过程中。
以SqlNode为例,其源码如下:
public interface SqlNode {
boolean apply(DynamicContext context);
// ...
}
public class MixedSqlNode implements SqlNode {
private List<SqlNode> contents;
// ...
@Override
public boolean apply(DynamicContext context) {
Iterator<SqlNode> iter = contents.iterator();
while (iter.hasNext()) {
SqlNode sqlNode = iter.next();
sqlNode.apply(context);
}
return true;
}
}
在MyBatis中,SQL语句的执行由对SqlNode的应用开展,存储SQL语句的主干是MixedSqlNode作为容器,SqlNode则表示叶子节点。在SqlNode接口中定义了apply方法,该方法对节点进行处理,而在MixedSqlNode中则通过迭代处理了其内部维护的SqlNode节点。
示例一:文件系统的案例
假设我们要实现一个文件系统,其中各个目录和文件节点是可以随意嵌套的。其中,文件节点可以进行读写操作,而目录节点则可以管理它包含的子节点,并对其进行各种基本操作。
下面是用Java代码实现的文件系统:
// Component: 抽象文件系统节点
public abstract class FileSystemNode {
public abstract void add(FileSystemNode node);
public abstract void remove(FileSystemNode node);
public abstract String getName();
public abstract int getSize();
public String getInfo() {
return getName() + " (" + getSize() + ")";
}
}
// Leaf: 文件节点
public class FileNode extends FileSystemNode {
private String name;
private int size;
public FileNode(String name, int size) {
this.name = name;
this.size = size;
}
public void add(FileSystemNode node) {
throw new UnsupportedOperationException();
}
public void remove(FileSystemNode node) {
throw new UnsupportedOperationException();
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
}
// Composite: 目录节点
public class DirectoryNode extends FileSystemNode {
private String name;
private List<FileSystemNode> nodes = new ArrayList<>();
public DirectoryNode(String name) {
this.name = name;
}
public void add(FileSystemNode node) {
nodes.add(node);
}
public void remove(FileSystemNode node) {
nodes.remove(node);
}
public String getName() {
return name;
}
public int getSize() {
int size = 0;
for (FileSystemNode node : nodes) {
size += node.getSize();
}
return size;
}
public List<FileSystemNode> getNodes() {
return nodes;
}
}
// Client: 测试类
public class FileSystemClient {
public static void main(String[] args) {
// 创建文件节点
FileSystemNode file1 = new FileNode("file1.txt", 10);
FileSystemNode file2 = new FileNode("file2.txt", 20);
FileSystemNode file3 = new FileNode("file3.txt", 30);
// 创建目录节点
DirectoryNode dir1 = new DirectoryNode("dir1");
DirectoryNode dir2 = new DirectoryNode("dir2");
// 添加文件节点到目录节点
dir1.add(file1);
dir1.add(file2);
dir2.add(file3);
// 添加目录节点到根节点目录
DirectoryNode root = new DirectoryNode("root");
root.add(dir1);
root.add(dir2);
// 输出各个节点信息
printNodeInfo(root);
}
private static void printNodeInfo(FileSystemNode node) {
System.out.println(node.getInfo());
if (node instanceof DirectoryNode) {
for (FileSystemNode sub : ((DirectoryNode) node).getNodes()) {
printNodeInfo(sub);
}
}
}
}
在上述代码中,FileSystemNode作为抽象构件类,FileNode和DirectoryNode则作为具体构件类实现。在DirectoryNode中,通过List维护了一个子节点列表,并在add和remove方法中对其进行更新,同时在getSize方法中计算了目录节点的大小。在FileSystemClient中对各个节点进行初始化,并通过递归地方式遍历输出节点信息。
示例二:公司组织结构的案例
假设我们要实现一个公司组织结构,其中员工和部门可以任意嵌套,员工节点可以获取和修改自己的信息,部门节点则可以管理它包含的部门和员工,并对其进行各种基本操作。
下面是用Java代码实现的公司组织结构:
// Component: 抽象组织节点
public abstract class OrganizationNode {
protected String name;
public OrganizationNode(String name) {
this.name = name;
}
public abstract void add(OrganizationNode node);
public abstract void remove(OrganizationNode node);
public abstract List<OrganizationNode> getChildren();
public abstract String getInfo();
public String getName() {
return name;
}
}
// Leaf: 员工节点
public class EmployeeNode extends OrganizationNode {
private String title;
public EmployeeNode(String name, String title) {
super(name);
this.title = title;
}
public void add(OrganizationNode node) {
throw new UnsupportedOperationException();
}
public void remove(OrganizationNode node) {
throw new UnsupportedOperationException();
}
public List<OrganizationNode> getChildren() {
return Collections.emptyList();
}
public String getInfo() {
return "Employee " + name + " (" + title + ")";
}
// 获取和修改员工信息的方法
// ...
}
// Composite: 部门节点
public class DepartmentNode extends OrganizationNode {
private List<OrganizationNode> children = new ArrayList<>();
public DepartmentNode(String name) {
super(name);
}
public void add(OrganizationNode node) {
children.add(node);
}
public void remove(OrganizationNode node) {
children.remove(node);
}
public List<OrganizationNode> getChildren() {
return children;
}
public String getInfo() {
return "Department " + name;
}
}
// Client: 测试类
public class OrganizationClient {
public static void main(String[] args) {
// 创建员工节点
OrganizationNode employee1 = new EmployeeNode("001", "CEO");
OrganizationNode employee2 = new EmployeeNode("002", "CFO");
OrganizationNode employee3 = new EmployeeNode("003", "CTO");
// 创建部门节点
OrganizationNode development = new DepartmentNode("Development");
OrganizationNode finance = new DepartmentNode("Finance");
OrganizationNode marketing = new DepartmentNode("Marketing");
// 添加员工节点到部门节点
development.add(employee3);
finance.add(employee2);
marketing.add(employee1);
// 添加部门节点到总节点
OrganizationNode root = new DepartmentNode("Company");
root.add(development);
root.add(finance);
root.add(marketing);
// 打印组织结构信息
printNodeInfo(root, "");
}
private static void printNodeInfo(OrganizationNode node, String prefix) {
System.out.println(prefix + node.getInfo());
for (OrganizationNode sub : node.getChildren()) {
printNodeInfo(sub, prefix + "\t");
}
}
}
在上述代码中,OrganizationNode作为抽象构件类,EmployeeNode和DepartmentNode则作为具体构件类实现。在DepartmentNode中,通过List维护了一个子节点列表,并在add和remove方法中对其进行更新,同时在getChildren方法中返回节点的所有子节点。在OrganizationClient中对各个节点进行初始化,并通过递归地方式遍历输出节点信息。同时,EmployeeNode中实现了获取和修改员工信息的方法。
以上是组合模式及其在JDK和MyBatis源码中的运用详解,代码中的示例都展示了组合模式的核心特点:将节点组织成树形结构,使其更容易被管理和操作。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解 - Python技术站