Fork me on GitHub

【趣味设计模式系列】之【组合模式】

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 缺点

组合模式不容易限制组合中的构件。

posted @ 2022-03-02 22:35  小猪爸爸  阅读(583)  评论(0编辑  收藏  举报