【趣味設計模式系列】之【組合模式】
阿新 • • 發佈:2022-03-03
1. 簡介
組合模式(Composite Pattern):將物件組合成樹形結構以表示部分-整體的層次關係。
2. 示例
假設要設計一個檔案系統的目錄,需要靈活的在某個目錄下新增、刪除目錄或檔案,統計指定目錄下的檔案個數,計算指定目錄下的檔案大小。
設計類圖如下:
抽象類Node
package com.wzj.composite; /** * @Author: wzj * @Date: 2020/9/23 15:33 * @Desc: */ public abstract class Node { //檔案路徑 protected String path; public Node(String path) { this.path = path; } public String getPath() { return path; } // 統計目錄下檔案數目 public abstract int countNumOfFiles(); // 統計目錄下檔案大小 public abstract long countSizeOfFiles(); // 列印路徑 public abstract void print(); }
檔案類FileNode
package com.wzj.composite; import java.io.File; /** * @Author: wzj * @Date: 2020/9/23 16:38 * @Desc: */ public class FileNode extends Node { public FileNode(String path) { super(path); } @Override public String getPath() { return super.getPath(); } @Override public int countNumOfFiles() { return 1; } @Override public long countSizeOfFiles() { File file = new File(path); if (!file.exists()) return 0; return file.length(); } @Override public void print() { System.out.println(path); } }
目錄類DirectorNode
package com.wzj.composite; import java.util.ArrayList; import java.util.List; /** * @Author: wzj * @Date: 2020/9/23 16:48 * @Desc: */ public class DirectoryNode extends Node{ public List<Node> list = new ArrayList<>(); public DirectoryNode(String path) { super(path); } @Override public String getPath() { return super.getPath(); } @Override public int countNumOfFiles() { int num = 0; for (Node node : list) { num += node.countNumOfFiles(); } return num; } @Override public long countSizeOfFiles() { long size = 0; for (Node node : list) { size += node.countSizeOfFiles(); } return size; } @Override public void print() { System.out.println(path); } public void addSubNode(Node node) { list.add(node); } public void removeSubNode(Node node) { int size = list.size(); int i = 0; for (; i < size; ++i) { if (list.get(i).getPath().equalsIgnoreCase(node.getPath())) { break; } } if (i < size) { list.remove(i); } } }
客戶端Client
package com.wzj.composite;
/**
* @Author: wzj
* @Date: 2020/9/23 20:44
* @Desc:
*/
public class Client {
public static void main(String[] args) {
DirectoryNode root = new DirectoryNode("root");
DirectoryNode chapter1 = new DirectoryNode("chapter1");
DirectoryNode chapter2 = new DirectoryNode("chapter2");
Node r1 = new FileNode("r1.txt");
Node c11 = new FileNode("c11.txt");
Node c12 = new FileNode("c12.txt");
DirectoryNode b21 = new DirectoryNode("section21");
Node c211 = new FileNode("c211.txt");
Node c212 = new FileNode("c212.txt");
root.addSubNode(chapter1);
root.addSubNode(chapter2);
root.addSubNode(r1);
chapter1.addSubNode(c11);
chapter1.addSubNode(c12);
chapter2.addSubNode(b21);
b21.addSubNode(c211);
b21.addSubNode(c212);
printTree(root, 0);
System.out.println("root files num:" + root.countNumOfFiles());
System.out.println("/root/chapter1/ files num:" + chapter2.countNumOfFiles());
}
// 列印樹狀結構
public static void printTree(Node root, int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("--");
}
root.print();
if(root instanceof DirectoryNode) {
for (Node n : ((DirectoryNode)root).list) {
printTree(n, depth + 1);
}
}
}
}
結果
root
--chapter1
----c11.txt
----c12.txt
--chapter2
----section21
------c211.txt
------c212.txt
--r1.txt
root files num:5
/root/chapter1/ files num:2
3. 原始碼分析
SpringMVC中對引數的解析使用的是HandlerMethodArgumentResolver
介面,該類有一個實現類為HandlerMethodArgumentResolverComposite
,為組合類,又持有其他HandlerMethodArgumentResolver
物件,在它的實現方法中是對其他組合模式中的節點進行迴圈處理,從而選擇最適合的一個。
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
// 對引數解析器的引用
private final List<HandlerMethodArgumentResolver> argumentResolvers =
new LinkedList<HandlerMethodArgumentResolver>();
// 對其所擁有的物件迴圈,找到最適合的引數解析器
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
parameter.getGenericParameterType() + "]");
}
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
4. 總結
4.1 優點
- 高層呼叫簡單
一棵樹形機構中的所有節點都是Component,區域性和整體對呼叫者來說沒有任何區別,也就是說,高層模組不必關心自己處理的是單個物件還是整個組合結構,簡化了高層模組的程式碼。 - 節點自由增加
使用了組合模式後,我們可以看看,如果想增加一個樹枝節點、樹葉節點是不是都很容易,只要找到它的父節點就成,非常容易擴充套件,符合開閉原則,對以後的維護非常有利。
4.2 缺點
組合模式不容易限制組合中的構件。