首页 > 其他分享 >组合模式详解

组合模式详解

时间:2023-06-26 18:34:28浏览次数:50  
标签:name 组合 File void 模式 详解 file branch public

简介

组合模式(Composite)是针对由多个节点对象(部分)组成的树形结构的对象(整体)而发展出的一种结构型设计模式,它能够使客户端在操作整体对象或者其下的每个节点对象时做出统一的响应,保证树形结构对象使用方法的一致性,使客户端不必关注对象的整体或部分,最终达到对象复杂的层次结构与客户端解耦的目的。

组合模式的核心思想是将对象看作是一个树形结构,其中每个节点可以是一个单独的对象(叶子节点)或者一个包含其他节点的容器(组合节点)。叶子节点和组合节点都实现了相同的接口,这样客户端就可以对它们进行一致的操作,而不需要关心它们的具体类型。

组合模式有以下几个角色:

组合模式

  • Component(组件接口):所有复合节点与叶节点的高层抽象,定义出需要对组件操作的接口标准。对应本章例程中的抽象节点类,具体使用接口还是抽象类需根据具体场景而定。
  • Composite(复合组件):包含多个子组件对象(可以是复合组件或叶端组件)的复合型组件,并实现组件接口中定义的操作方法。对应本章例程中作为“根节点/枝节点”的文件夹类。
  • Leaf(叶端组件):不包含子组件的终端组件,同样实现组件接口中定义的操作方法。对应本章例程中作为“叶节点”的文件类
  • Client(客户端):按所需的层级关系部署相关对象并操作组件接口所定义的接口,即可遍历树结构上的所有组件。

好处和坏处

组合模式的好处有:

  • 可以将对象组合成树形结构,表示整体-部分的层次关系,符合人们的直觉。
  • 可以统一处理单个对象和对象组合,简化了客户端的代码逻辑,提高了系统的可复用性。
  • 可以遵循开闭原则,扩展性高,增加新的节点类型时不需要修改原有代码。

组合模式的坏处有:

  • 可以使设计变得过于抽象,不利于理解和维护。
  • 可以违反单一职责原则,让叶子节点和组合节点具有相同的接口,导致叶子节点出现不必要的方法。
  • 可以导致递归调用过深,影响系统的性能。

应用场景

组合模式是一种将对象组合成树形结构的设计模式,它可以表示整体-部分的层次关系,并且提供了一致的接口来操作单个对象和对象组合。应用场景有:

  • 当需要表示一个对象整体与部分的层次结构时,可以使用组合模式来实现树形结构。例如,文件系统中的文件与文件夹、组织机构中的部门与员工、商品分类中的类别与商品等。
  • 当需要统一处理单个对象和对象组合时,可以使用组合模式来实现多态性。例如,图形界面中的简单控件与容器控件、菜单系统中的菜单项与子菜单、报表系统中的单元格与表格等。
  • 当需要将对象的创建和使用分离时,可以使用组合模式来实现依赖注入。例如,Spring框架中的Bean对象与BeanFactory对象、测试框架中的测试用例与测试套件等。

Java 代码示例

假设我们有一个文件系统,其中有两种类型的文件:文本文件和文件夹。文本文件是叶子节点,文件夹是组合节点,可以包含其他文件。我们想要使用组合模式来实现文件系统的层次结构,并且提供一个打印文件路径的方法。代码如下:

定义抽象组件

public interface File {
    // 获取文件名称
    String getName();
    // 添加子文件
    void add(File file);
    // 删除子文件
    void remove(File file);
    // 获取子文件
    List<File> getChildren();
    // 打印文件路径
    void printPath(int space);
}

定义叶子节点

public class TextFile implements File {
    private String name;

    public TextFile(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void add(File file) {
        throw new UnsupportedOperationException("Text file cannot add child file");
    }

    @Override
    public void remove(File file) {
        throw new UnsupportedOperationException("Text file cannot remove child file");
    }

    @Override
    public List<File> getChildren() {
        throw new UnsupportedOperationException("Text file has no child file");
    }

    @Override
    public void printPath(int space) {
        StringBuilder sp = new StringBuilder();
        for (int i = 0; i < space; i++) {
            sp.append(" ");
        }
        System.out.println(sp + name);
    }
}

定义组合节点

public class Folder implements File {
    private String name;
    private List<File> children;

    public Folder(String name) {
        this.name = name;
        children = new ArrayList<>();
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void add(File file) {
        children.add(file);
    }

    @Override
    public void remove(File file) {
        children.remove(file);
    }

    @Override
    public List<File> getChildren() {
        return children;
    }

    @Override
    public void printPath(int space) {
        StringBuilder sp = new StringBuilder();
        for (int i = 0; i < space; i++) {
            sp.append(" ");
        }
        System.out.println(sp + name);
        space += 2;
        for (File child : children) {
            child.printPath(space);
        }
    }
}

客户端代码

public class Client {
    public static void main(String[] args) {
        // 创建一个根文件夹,并添加两个文本文件和一个子文件夹
        File root = new Folder("root");
        root.add(new TextFile("a.txt"));
        root.add(new TextFile("b.txt"));
        File subFolder = new Folder("subFolder");
        root.add(subFolder);

        // 在子文件夹中添加两个文本文件
        subFolder.add(new TextFile("c.txt"));
        subFolder.add(new TextFile("d.txt"));

        // 打印根文件夹的路径
        root.printPath(0);
    }
}

输出结果:

root
  a.txt
  b.txt
  subFolder
    c.txt
    d.txt

Go 代码示例

package main

// importing fmt package
import (
	"fmt"
)

// IComposite interface
type IComposite interface {
	perform()
}

// Leaflet struct
type Leaflet struct {
	name string
}

// Leaflet class method perform
func (leaf *Leaflet) perform() {

	fmt.Println("Leaflet " + leaf.name)
}

// Branch struct
type Branch struct {
	leafs    []Leaflet
	name     string
	branches []Branch
}

// Branch class method perform
func (branch *Branch) perform() {

	fmt.Println("Branch: " + branch.name)
	for _, leaf := range branch.leafs {
		leaf.perform()
	}

	for _, branch := range branch.branches {
		branch.perform()
	}
}

// Branch class method add leaflet
func (branch *Branch) add(leaf Leaflet) {
	branch.leafs = append(branch.leafs, leaf)

}

//Branch class method addBranch branch
func (branch *Branch) addBranch(newBranch Branch) {

	branch.branches = append(branch.branches, newBranch)
}

//Branch class  method getLeaflets
func (branch *Branch) getLeaflets() []Leaflet {
	return branch.leafs
}

// main method
func main() {

	var branch = &Branch{name: "branch 1"}

	var leaf1 = Leaflet{name: "leaf 1"}
	var leaf2 = Leaflet{name: "leaf 2"}

	var branch2 = Branch{name: "branch 2"}

	branch.add(leaf1)
	branch.add(leaf2)
	branch.addBranch(branch2)

	branch.perform()

}

输出结果:

G:\GoLang\examples>go run composite.go
Branch: branch 1
Leaflet leaf 1
Leaflet leaf 2
Branch: branch 2

Spring 代码示例

Spring 框架也可以使用组合模式来实现对象的层次结构,它提供了一个注解叫做 @Component,它可以用来标注一个类是一个组件,即一个可被Spring管理的Bean对象。@Component 注解有一个属性叫做value,它可以用来指定组件的名称。我们可以使用 @Component 注解来标注我们的文件类,然后在配置文件或注解中声明这些组件,Spring 就会自动创建和管理这些组件对象。

假设我们有一个文件系统,其中有两种类型的文件:文本文件和文件夹。文本文件是叶子节点,文件夹是组合节点,可以包含其他文件。我们想要使用组合模式来实现文件系统的层次结构,并且提供一个打印文件路径的方法。我们可以使用 @Component 注解来实现,代码如下:

抽象组件不用改造

public interface File {
    // 获取文件名称
    String getName();
    // 添加子文件
    void add(File file);
    // 删除子文件
    void remove(File file);
    // 获取子文件
    List<File> getChildren();
    // 打印文件路径
    void printPath();
}

叶子节点添加 @Component("a.txt") 注解

@Component("a.txt")
public class TextFile implements File {
    private String name;

    public TextFile() {
        this.name = "a.txt";
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void add(File file) {
        throw new UnsupportedOperationException("Text file cannot add child file");
    }

    @Override
    public void remove(File file) {
        throw new UnsupportedOperationException("Text file cannot remove child file");
    }

    @Override
    public List<File> getChildren() {
        throw new UnsupportedOperationException("Text file has no child file");
    }

    @Override
    public void printPath() {
        System.out.println(name);
    }
}

组合节点添加 @Component("root") 注解

@Component("root")
public class Folder implements File {
    private String name;
    private List<File> children;

    // 通过@Autowired注解自动注入所有类型为File的Bean对象到children集合中
    @Autowired
    public Folder(List<File> children) {
        this.name = "root";
        this.children = children;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void add(File file) {
        children.add(file);
    }

    @Override
    public void remove(File file) {
        children.remove(file);
    }

    @Override
    public List<File> getChildren() {
        return children;
    }

    @Override
    public void printPath() {
        System.out.println(name);
        for (File child : children) {
            child.printPath();
        }
    }
}

SpringBoot 测试代码

@Slf4j
@SpringBootTest
class SpringBootTest {

    @Autowired
    private Folder folder;

    @Test
    void test() {
        folder.printPath();
    }
}

输出结果:

root
a.txt

总结

组合模式是一种常用的结构型设计模式,它可以将对象组合成树形结构,并且提供了一致的接口来操作单个对象和对象组合,是一种值得学习和掌握的设计模式。

关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!

标签:name,组合,File,void,模式,详解,file,branch,public
From: https://www.cnblogs.com/waynaqua/p/17506458.html

相关文章

  • F5APM第七期Network Access模式配置
    F5APM第七期NetworkAccess模式配置’......
  • WebRTC-STUN协议详解
    1、STUN协议概述STUN(SessionTraversalUtilitiesforNAT)NAT会话穿透工具,STUN是一个Client/Server协议,支持请求/响应类型、指示类型两种类型。STUN作为ICE(InteractiveConnectivityEstablishment,交互式连接建立)解决方案的一种工具使用,STUN协议本身没有穿透等能力,只是为穿透提供反......
  • F5APM第六期Portal Access模式配置
    F5APM第六期PortalAccess模式配置Portal_conn......
  • 一文掌握设计模式(定义+UML类图+应用)
    一、引子从学编程一开始就被告知,要想做一名优秀的程序员两大必要技能:1.源码阅读(JDK、C等底层语言封装)2.设计模式(使用某种语言优雅的落地典型场景功能)。一般随着工作年限的增长,被迫对底层语言/框架源码阅读的越来愈多,但是设计模式如不刻意去学习,永远不会真正掌握。笔者把设计......
  • 详解自动化面试常见算法题!!
    1、实现一个数字的反转,比如输入12345,输出54321num=12345num_str=str(num)reversed_num_str=num_str[::-1]reversed_num=int(reversed_num_str)print(reversed_num)#输出54321代码解析:首先将输入的数字转换为字符串,然后使用切片操作将字符串反转,最后再将反转......
  • 结构型模式-静态代理模式
    #模式类型:结构性模型#模型方法:静态代理模式#作用:在不修改目标代码的情况下,可以控制目标代码的访问,可以在其前后添加自己的业务代码#使用场景:监控。日志,限流fromabcimportABCMeta,abstractmethodclassITeacher(metaclass=ABCMeta):"""教师接口"""@abs......
  • epoll的两种触发模式
    epoll有两种模式,EdgeTriggered(简称ET)和LevelTriggered(简称LT).在采用这两种模式时要注意的是,如果采用ET模式,那么仅当状态发生变化时才会通知,而采用LT模式类似于原来的select/poll操作,只要还有没有处理的事件就会一直通知. 以代码来说明问题: 首先给出server的代......
  • traceroute命令详解
    目录traceroute命令详解traceroute命令详解traceroute命令用于显示数据包到主机间的路径,预设数据包大小是40Bytes。安装命令: yuminstalltraceroute命令格式: traceroute[-46dFITUnreAV][-f存活数值][-g网关][-i网络界面][-m存活数值][-p端口][-s来源地址][-q......
  • cpuinfo详解
    目录cpuinfo详解1、通过cat/proc/cpuinfo查看2、通过lscpu命令进行查看3、逻辑核数、物理cpu、线程数关系4、查看cpu相关信息命令cpuinfo详解linux系统,cpu相关信息是保存在中/proc/cpuinfo文件中。1、通过cat/proc/cpuinfo查看[root@root~]#cat/proc/cpuinfoproces......
  • meminfo详解
    目录meminfo详解1、通过cat/proc/meminfo查看2、查看显示内存状态:free[option][-s<间隔秒数>]3、查看虚拟内存使用状态:vmstat[option]4、清理缓存meminfo详解Linux系统内存使用状况主要存储在/proc/meminfo中,”free”、”vmstat”等命令就是通过它获取数据的。1、通过......