golang实现设计模式之组合模式-优缺点与适用场景

发布时间 2023-06-09 18:22:40作者: 进击的davis

组合模式是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式。

结构

  • 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
  • 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
  • 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

优缺点

  • 优点

1.使用与实现分离,客户端不用关注具体的组合方式,只关心使用。组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码
2.符合开闭原则。更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”。

  • 缺点

1.设计较复杂,客户端需要花更多时间理清类之间的层次关系。
2.不容易限制容器中的构件。
3.不容易用继承的方法来增加构件的新功能。

适用场景

  • 1.在需要表示一个对象整体与部分的层次结构的场合。
  • 2.要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。

类型

  • 1.透明式。由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
  • 2.安全式。将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。

代码实现

package main

import "fmt"

/*
业务场景:
- 在操作系统中的文件系统,我们可以区分文件夹和文件,文件夹又包含文件夹或文件,
  如我们需要在某个特定文件夹内的所有文件搜索某个关键词,就可以用到该模式。
 */

// 1.composite interface,声明方法,叶子节点和非叶子节点实现接口
// case中,文佳认为是叶子节点,文件夹是非叶子节点
type Component interface {
   Search(keyword string)
}

// 2.leaf node,叶子节点,不能向下递归
type File struct {
   Name string
}

func (r *File) Search(keyword string)  {
   fmt.Printf("Searching for keyword %s in file %s\n", keyword, r.Name)
}

func (r *File) GetName() string {
   return r.Name
}

// 3.component,可以包含叶子节点
type Folder struct {
   components []Component
   Name string
}

func (r *Folder) Search(keyword string)  {
   fmt.Printf("Searching for keyword %s in file %s\n", keyword, r.Name)
   for _, component := range r.components {
      component.Search(keyword)
   }
}

func (r *Folder) Add(c Component)  {
   r.components = append(r.components, c)
}

// 4.client
func main()  {
   // create 3 files
   file1 := &File{"file1"}
   file2 := &File{"file2"}
   file3 := &File{"file3"}

   // create 3 folders
   folder1 := &Folder{Name: "folder1"}
   folder2 := &Folder{Name: "folder2"}
   folder3 := &Folder{Name: "folder3"}

   // composite folder and file
   // folder1 as root
   folder1.Add(folder2)
   folder1.Add(folder3)
   folder2.Add(file1)
   folder2.Add(file2)
   folder3.Add(file3)

   // search keyword in root
   folder1.Search("hello")
   /*
   Searching for keyword hello in file folder1
   Searching for keyword hello in file folder2
   Searching for keyword hello in file file1
   Searching for keyword hello in file file2
   Searching for keyword hello in file folder3
   Searching for keyword hello in file file3
    */
}

参考文章: