跳转到内容

组合模式

50% developed
来自维基教科书,开放的书籍,开放的世界

命令模式 计算机科学设计模式
组合模式
装饰模式

组合设计模式降低了处理以树形结构表示数据的实现成本。当应用程序对树进行处理时,通常需要处理对组件的迭代,在树上的移动,以及分别处理节点和叶子。所有这些都会产生大量的代码。假设您需要处理一个文件系统存储库。每个文件夹可以包含文件或文件夹。为了处理这种情况,您有一个包含文件或文件夹的项目数组。现在您需要在整个文件夹树上实现一个文件搜索操作。伪代码应该如下所示

method searchFilesInFolders(rootFolder, searchedFileName) is
    input: a list of the content of the rootFolder.
    input: the searchedFileName that should be found in the folders.
    output: the list of encountered files.

  Empty the foundFiles list
  Empty the parentFolders list
  Empty the parentIndices list
  currentFolder := rootFolder
  currentIndex := 0
  Add rootFolder to parentFolders

  while parentFolders is not empty do
    if currentIndex is out of currentFolder then
        currentFolder := last item of parentFolders
        Remove the last item of parentFolders
        currentIndex := last item of parentIndices + 1
        Remove the last item of parentIndices

    else if the item at the currentIndex of the currentFolder is a folder then
      currentFolder := the folder
      Add currentFolder to parentFolders
      Add currentIndex to parentIndices
      currentIndex := 0

    otherwise
      if the name of the file is equal to the searchedFileName then
        Add the file to foundFiles
      Increment currentIndex

  Return the foundFiles

在前面的代码中,同一个 while 循环的每次迭代都是对一个节点或叶子的处理。在处理完成后,代码会移动到下一个要处理的节点或叶子的位置。循环中有三个分支。第一个分支在处理完节点的所有子节点后为真,然后它移动到父节点,第二个分支进入子节点,最后一个分支处理叶子(即文件)。应该存储位置的内存以便返回到树。这种实现的问题是可读性差,而且文件夹和文件的处理是完全分开的。这段代码维护起来很麻烦,您需要时刻考虑到整棵树。文件夹和文件应该以相同的方式调用,所以它们应该是实现相同接口的对象。

UML 中的组合模式。
组件
  • 是对所有组件(包括复合组件)的抽象。
  • 声明组合中对象的接口。
  • (可选)定义一个接口来访问组件在递归结构中的父节点,并在适当的情况下实现它。
叶子
  • 表示组合中的叶子对象。
  • 实现所有组件方法。
组合模式
  • 表示一个复合组件(包含子节点的组件)。
  • 实现操作子节点的方法。
  • 实现所有组件方法,通常通过将它们委托给它的子节点来实现。

所以现在的实现更像是这样

interface FileSystemComponent is
  method searchFilesInFolders(searchedFileName) is
      input: the searchedFileName that should be found in the folders.
      output: the list of encountered files.
class File implementing FileSystemComponent is
  method searchFilesInFolders(searchedFileName) is
      input: the searchedFileName that should be found in the folders.
      output: the list of encountered files.

    if the name of the file is equal to the searchedFileName then
      Empty the foundFiles list
      Add the file to foundFiles
      Return the foundFiles

    otherwise
      Return an empty list
class Folder implementing FileSystemComponent is
  field children is
    The list of the direct children.

  method searchFilesInFolders(searchedFileName) is
      input: the searchedFileName that should be found in the folders.
      output: the list of encountered files.

    Empty the foundFiles list

    for each child in children
      Call searchFilesInFolders(searchedFileName) on the child
      Add the result to foundFiles

    Return the foundFiles

正如您所看到的,一个组件可以是一个单独的对象,也可以是对象的集合。组合模式可以同时表示这两个条件。在这个模式中,可以开发树结构来表示部分-整体层次结构。

示例

这种模式最典型的应用就是图形用户界面。界面的窗口部件被组织成树状结构,对所有窗口部件的操作(调整大小、重新绘制等)都是使用组合设计模式进行处理的。

成本

这种模式是最便宜的模式之一。您可以在每次需要处理数据树时实现它,而无需担心。没有这种模式的错误用法。该模式的成本只是处理复合节点的子节点,但这项成本在没有设计模式的情况下是必需的,而且更昂贵。

创建

您需要创建一个几乎为空的接口,并实现对复合子节点的管理。这个成本非常低。

维护

您不会陷入系统中。唯一相对昂贵的情况是您必须经常更改应用于整个数据树的操作。

移除

您应该在移除数据树时移除该模式。因此您只需要一次性移除所有内容。这个成本非常低。

建议

  • 在类的名称中加入复合组件这两个词,以便向其他开发人员表明该模式的使用。

实现

组合模式的各种示例。

C# 中的实现
using System;
using System.Collections.Generic;
namespace Composite
{
    class Program
    {
        interface IGraphic
        {
            void Print();
        }
        class CompositeGraphic : IGraphic
        {
            private List<IGraphic> child = new List<IGraphic>();
            public CompositeGraphic(IEnumerable<IGraphic> collection)
            {
                child.AddRange(collection);
            }
            public void Print()
            {
                foreach(IGraphic g in child)
                {
                    g.Print();
                }
            }
        }
        class Ellipse : IGraphic
        {
            public void Print()
            {
                Console.WriteLine("Ellipse");
            }
        }
        static void Main(string[] args)
        {
            new CompositeGraphic(new IGraphic[]
                {
                    new CompositeGraphic(new IGraphic[]
                        {
                            new Ellipse(),
                            new Ellipse(),
                            new Ellipse()
                        }),
                    new CompositeGraphic(new IGraphic[]
                        {
                            new Ellipse()
                        })
                }).Print();
        }
    }
}
Common Lisp 中的实现

以下用 Common Lisp 编写的示例直接从下面的 Java 示例翻译而来,它实现了一个名为print-graphic的方法,可以用于椭圆,也可以用于元素是列表或椭圆的列表。

(defstruct ellipse) ;; An empty struct.
;; For the method definitions, "object" is the variable,
;; and the following word is the type.
(defmethod print-graphic ((object null))
  NIL)
(defmethod print-graphic ((object cons))
  (print-graphic (first object))
  (print-graphic (rest object)))
(defmethod print-graphic ((object ellipse))
  (print 'ELLIPSE))
(let* ((ellipse-1 (make-ellipse))
       (ellipse-2 (make-ellipse))
       (ellipse-3 (make-ellipse))
       (ellipse-4 (make-ellipse)))
  (print-graphic (cons (list ellipse-1 (list ellipse-2 ellipse-3)) ellipse-4)))
Java 中的实现

以下用 Java 编写的示例实现了一个图形类,它可以是一个椭圆,也可以是多个图形的组合。每个图形都可以被打印。用 Backus-Naur 形式,

Graphic ::= ellipse | GraphicList
GraphicList ::= empty | Graphic GraphicList

可以扩展它来实现其他几种形状(矩形等)和方法(平移等)。

import java.util.List;
import java.util.ArrayList;

/** "Component" */
interface Graphic {
    //Prints the graphic.
    public void print();
}

/** "Composite" */
class CompositeGraphic implements Graphic {
    //Collection of child graphics.
    private final List<Graphic> childGraphics = new ArrayList<>();

    //Adds the graphic to the composition.
    public void add(Graphic graphic) {
        childGraphics.add(graphic);
    }
    
    //Prints the graphic.
    @Override
    public void print() {
        for (Graphic graphic : childGraphics) {
            graphic.print();  //Delegation
        }
    }
}

/** "Leaf" */
class Ellipse implements Graphic {
    //Prints the graphic.
    @Override
    public void print() {
        System.out.println("Ellipse");
    }
}

/** Client */
class CompositeDemo {
    public static void main(String[] args) {
        //Initialize four ellipses
        Ellipse ellipse1 = new Ellipse();
        Ellipse ellipse2 = new Ellipse();
        Ellipse ellipse3 = new Ellipse();
        Ellipse ellipse4 = new Ellipse();

        //Creates two composites containing the ellipses
        CompositeGraphic compositGraphic2 = new CompositeGraphic();
        compositGraphic2.add(ellipse1);
        compositGraphic2.add(ellipse2);
        compositGraphic2.add(ellipse3);
        
        CompositeGraphic compositGraphic3 = new CompositeGraphic();
        compositGraphic3.add(ellipse4);
        
        //Create another graphics that contains two graphics
        CompositeGraphic compositGraphic = new CompositeGraphic();
        compositGraphic.add(compositGraphic2);
        compositGraphic.add(compositGraphic3);

        //Prints the complete graphic (Four times the string "Ellipse").
        compositGraphic.print();
    }
}

以下用 Java 编写的示例实现了一个图形类,它可以是一个椭圆,也可以是多个图形的组合。每个图形都可以被打印。用代数形式,

       Graphic = ellipse | GraphicList
       GraphicList = empty | Graphic GraphicList

可以扩展它来实现其他几种形状(矩形等)和方法(平移等)。

/** "Component" */
interface Graphic {
    // Prints the graphic.
    public void print();
}
/** "Composite" */
import java.util.List;
import java.util.ArrayList;
class CompositeGraphic implements Graphic {
    // Collection of child graphics.
    private List<Graphic> mChildGraphics = new ArrayList<Graphic>();
    // Prints the graphic.
    public void print() {
        for (Graphic graphic : mChildGraphics) {
            graphic.print();
        }
    }
    // Adds the graphic to the composition.
    public void add(Graphic graphic) {
        mChildGraphics.add(graphic);
    }
    // Removes the graphic from the composition.
    public void remove(Graphic graphic) {
        mChildGraphics.remove(graphic);
    }
}
/** "Leaf" */
class Ellipse implements Graphic {
    // Prints the graphic.
    public void print() {
        System.out.println("Ellipse");
    }
}
/** Client */
public class Program {
    public static void main(String[] args) {
        // Initialize four ellipses
        Ellipse ellipse1 = new Ellipse();
        Ellipse ellipse2 = new Ellipse();
        Ellipse ellipse3 = new Ellipse();
        Ellipse ellipse4 = new Ellipse();
        // Initialize three composite graphics
        CompositeGraphic graphic = new CompositeGraphic();
        CompositeGraphic graphic1 = new CompositeGraphic();
        CompositeGraphic graphic2 = new CompositeGraphic();
        // Composes the graphics
        graphic1.add(ellipse1);
        graphic1.add(ellipse2);
        graphic1.add(ellipse3);
        graphic2.add(ellipse4);
        graphic.add(graphic1);
        graphic.add(graphic2);
        // Prints the complete graphic (four times the string "Ellipse").
        graphic.print();
    }
}
PHP 中的实现
<?php
/** "Component" */
interface Graphic
{
    /**
     * Prints the graphic
     *
     * @return void
     */
    public function printOut();
}
 
/**
 * "Composite" - Collection of graphical components
 */
class CompositeGraphic implements Graphic
{
    /**
     * Collection of child graphics
     *
     * @var array
     */
    private $childGraphics = array();
 
    /**
     * Prints the graphic
     *
     * @return void
     */
    public function printOut()
    {
        foreach ($this->childGraphics as $graphic) {
            $graphic->printOut();
        }
    }
 
    /**
     * Adds the graphic to the composition
     *
     * @param Graphic $graphic Graphical element
     *
     * @return void  
     */
    public function add(Graphic $graphic)
    {
        $this->childGraphics[] = $graphic;
    }
 
    /**
     * Removes the graphic from the composition
     *
     * @param Graphic $graphic Graphical element
     *
     * @return void
     */
    public function remove(Graphic $graphic)
    {
        if (in_array($graphic, $this->childGraphics)) {
            unset($this->childGraphics[array_search($graphic, $this->childGraphics)]);
        }
    }
}
 
/** "Leaf" */
class Ellipse implements Graphic
{
    /**
     * Prints the graphic
     *
     * @return void
     */
    public function printOut()
    {
        echo "Ellipse";
    }
}
 
/** Client */
 
//Initialize four ellipses
$ellipse1 = new Ellipse();
$ellipse2 = new Ellipse();
$ellipse3 = new Ellipse();
$ellipse4 = new Ellipse();
 
//Initialize three composite graphics
$graphic = new CompositeGraphic();
$graphic1 = new CompositeGraphic();
$graphic2 = new CompositeGraphic();
 
//Composes the graphics
$graphic1->add($ellipse1);
$graphic1->add($ellipse2);
$graphic1->add($ellipse3);
 
$graphic2->add($ellipse4);
 
$graphic->add($graphic1);
$graphic->add($graphic2);
 
//Prints the complete graphic (four times the string "Ellipse").
$graphic->printOut();
Python 中的实现
class Component(object):
    def __init__(self, *args, **kw):
        pass
    def component_function(self): pass
class Leaf(Component):
    def __init__(self, *args, **kw):
        Component.__init__(self, *args, **kw)
    def component_function(self):
        print "some function"
class Composite(Component):
    def __init__(self, *args, **kw):
        Component.__init__(self, *args, **kw)
        self.children = []
   
    def append_child(self, child):
        self.children.append(child)
   
    def remove_child(self, child):
        self.children.remove(child)
    def component_function(self):
        map(lambda x: x.component_function(), self.children)
   
c = Composite()
l = Leaf()
l_two = Leaf()
c.append_child(l)
c.append_child(l_two)
c.component_function()
Ruby 中的实现
module Component
  def do_something
    raise NotImplementedError
  end
end
class Leaf
  include Component
  def do_something
    puts "Hello"
  end
end
class Composite
  include Component
  attr_accessor :children
  def initialize
    self.children = []
  end
  def do_something
    children.each {|c| c.do_something}
  end
  def append_child(child)
    children << child
  end
  def remove_child(child)
    children.delete child
  end
end
composite = Composite.new
leaf_one = Leaf.new
leaf_two = Leaf.new
composite.append_child(leaf_one)
composite.append_child(leaf_two)
composite.do_something


Clipboard

待办事项
添加更多插图。


命令模式 计算机科学设计模式
组合模式
装饰模式


您对本页有疑问?
在这里提问


在本图书上创建新页面


华夏公益教科书