组合设计模式

组合模式

组合模式和面向对象中“组合关系”完全是两码事,这里的组合模式主要用于处理结构为树形的数据。对于组合模式其应用场景比较特殊,这种模式在实际的项目开发中并不常见。但是一旦数据呈现出树形结构,这种模式就能发挥出很大的作用,可以让代码变得简洁。

组合模式的应用一:目录树

对于组合模式的定义是这样描述的:将一组对象组织成树形结构,以表示一种“部分—整体”的层次结构。

我们可以通过举例来解释组合模式。假设有这样一个需求:请设计一个类,表示文件系统中的目录,并能够方便实现以下的功能:

  • 动态添加、删除某个目录下的子目录或文件

  • 统计指定目录下的文件个数

  • 统计指定目录下文件的总大小

对于这个需求我们可以这样实现:

public class FileSystemNode{
    private String path;
    private boolean isFile;
    private List<FileSystemNode> subNodes = new ArrayList<>();
    
    public FileSystemNode(String path , boolean isFile){
        this.path = path;
        this.iSFile = isFile;
    }
    
    public int countNumOfFiles(){
        if(isFile)
            return 1;
        int numFiles = 0;
        for(FileSystemNode fileOrDir : subNodes)
            numFiles += fileOrDir.countNumOfFiles();
        return numOfFiles;    
    }
    
    public long countSizeOfFiles(){
        if(ifFile){
            File file = new File(path);
            if(!file.exists()) return 0;
            return file.length();
        }
        long sizeOfFiles = 0;
        for(FileSystemNode fileOrDir : subNodes)
            sizeOfFiles += fileOrDir.countSizeOfFiles();
        return sizeOfFiles;
    }
    public void addSubNode(FileSystemNode fileOrDir){
        subNodes.add(fileOrDir);
    }
    public String getPath(){
        return path;
    }
    public void removeSubNode(FileSystemNode fileOrDir){
        int size = subNodes.size();
        int i = 0;
        for(; i < size; ++i)
            if(subNodes.get(i).getPath().equalsIsIgnoreCase(fileOrDir.getPath()))    break;
        
        if(i<size)
            subNodes.remove(i);
    }

}

如果从单纯的能不能用来看,上面的代码已经是可以用了。但是,如果开发的是一个大系统,从扩展性(文件或目录可能对应着不同的操作)、业务建模(文件和目录在业务上是两个概念)和代码的可读性(文件和目录区分对待才能更加符合人们对业务的认知)的角度来说,最好是要将文件和目录区分开来,用File表示文件Directory表示文件夹。根据这个思路我们将代码进行重构;

//抽象基类
public abstract class FileSystemNode{
    protected String path;
    public FileSystemNode(String path){
        this.path = path;
    }
    public abstract int coun
tNumOfFiles();
    public abstract long countSizeOfFiles();
    public String getPath(){
        return path;    
    }
}


//文件类
public class File extends FileSystemNode{
    public File(String path){
        super(path);
    }
    @Override
    public int countNumOfFiles(){
        return 1;    
    }
    @Override
    public long countSizeOfFiles(){
        File file = new File(path);
        if(!file.exists())    return 0;
        return file.length();
    }
}

//文件夹类
public class Directory extends FileSystemNode{
    private List<FileSystemNode> subNodes = new ArrayList<>();
    public Directory(String path){
        super(path);
    }    
    @Override
    public int countNumOfFiles(){
        int numOfFile = 0;
        for(FileSystemNode fileOrDir : subNodes)
            numOfFile += fileOrDir.countNumOfFiles();
        return numOfFile;
    }

    @Override
    public long countSizeOfFiles(){
        long sizeOfFile = 0;
        for(FileSystemNode fileOrDir : subNodes)
            sizeOfFile += fileOrDir.countSizeOfFiles();
        return sizeOfFiles;    
    }
    public void addSubNode(FileSystemNode fileOrDir){
        subNodes.add(fileOrDir);
    }
    public void removeSubNode(FileSystemNode fileOrDir){
        int size = subNodes.size();
        int i = 0;
        for(;i < size; ++i)
            if(subNodes.get(i).getPath().equalsIngnoreCase(fileOrDir.getPath()))
                break;
        if(i<size)    subNodes.remove(i);
    }
}

实际上,组合模式与其说是一种设计模式,倒不如说是对业务场景的一种数据结构和算法的抽象。其中业务场景中的数据可以表示成树这种数据结构,业务需求可以通过树上的递归遍历算法实现。

组合模式的应用二:人力树

上面我们举了文件系统的例子,接下来我们再举一个人力树的例子。理解了这两个例子,就理解了组合模式。在实际的项目中,再遇到类似的例子,只要“依葫芦画瓢”就好了。

假设有一个OA系统,有员工和门两种类型的数据,部门可以包含部门和员工。要再内存中构建这个公式的组织结构图,并计算部门的工资成本。

很显然这两种数据类型可以抽象成一棵树,那么我们就可以使用组合设计模式。

具体的代码实现如下:

//人力资源抽象类
public abstract class HumanResource{
    protected long id;
    protected double salary;
    
    public HumanResource(long id){
        this.id = id;
    }
    public long getId(){
        return id;
    }
    public abstract double calculateSalary();
}


//职工类
class Employee extends HumanResource{
    public Employee(long id , double salary){
        super(id);
        this.salary = salary;
    }
    @Override
    public double calculateSalary(){
        return salary;
    }
}


//部门类
public Department extends HumanResource{
    private List<HumanResource> subNodes = new ArrayList<>();
    public Department(long id){
        super(id);    
    }
    
    @Override
    public double calculateSalary(){
        double totalSalary = 0;
        for(HumanResource hr : subNodes)
            totalSalary += hr.calculateSalary();
        this.salary = totalSalary;
        return this.salary;
    }
    
    public void addSubNode(HumanResource hr){
        subNodes.add(hr);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值