.Net结构型设计模式之组合模式(Composite)

目录
  • 一、动机(Motivate)
  • 二、意图(Intent)
  • 三、结构图(Structure)
  • 四、模式的组成
  • 五、组合模式的具体代码实现
    • 1、透明式的组合模式
    • 2、安全式的组合模式
  • 六、组合模式的实现要点:
    • 组合模式的优点:
    • 组合模式的缺点:
    • 在以下情况下应该考虑使用组合模式:
  • 七、.NET 中组合模式的实现

一、动机(Motivate)

在我们的操作系统中有文件夹的概念,文件夹可以包含文件夹,可以嵌套多层,最里面包含的是文件,这个概念和“俄罗斯套娃”很像。当然还有很多的例子,例如我们使用系统的时候,会使用到“系统菜单”,这个东西是树形结构。这些例子包含的这些东西或者说是对象,可以分为两类,一类是:容器对象,可以包含其他的子对象;另一类是:叶子对象,这类对象是不能在包含其他对象的对象了。在软件设计中,我们该怎么处理这种情况呢?是每类对象分别对待,还是提供一个统一的操作方式呢。组合模式给我们提供了一种解决此类问题的一个途径。

客户代码过多地依赖于对象容器(对象容器是对象的容器,细细评味)复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等方面的弊端。如何将“客户代码与复杂的对象容器结构”解耦?如何让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

二、意图(Intent)

将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。        ——  《设计模式》GoF

三、结构图(Structure)

四、模式的组成

组合模式中涉及到三个角色:
(1)、抽象构件角色(Component):这是一个抽象角色,它给参加组合的对象定义出了公共的接口及默认行为,可以用来管理所有的子对象(在透明式的组合模式是这样的)。在安全式的组合模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝结构对象给出。
(2)、树叶构件角色(Leaf):树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为。(原始对象的行为可以理解为没有容器对象管理子对象的方法,或者 【原始对象行为】+【管理子对象的行为(Add,Remove等)】=面对客户代码的接口行为集合)
(3)、树枝构件角色(Composite):代表参加组合的有下级子对象的对象,树枝对象给出所有管理子对象的方法实现,如Add、Remove等。
组合模式实现的最关键的地方是——简单对象和复合对象必须实现相同的接口。这就是组合模式能够将组合对象和简单对象进行一致处理的原因。

五、组合模式的具体代码实现

组合模式有两种实现方式,一种是:透明式的组合模式,另外一种是:安全式的组合模式

1、透明式的组合模式

指“抽象构件角色”定义的接口行为集合包含两个部分,一部分是叶子对象本身所包含的行为(比如Operation),另外一部分是容器对象本身所包含的管理子对象的行为(Add,Remove)。这个抽象构件必须同时包含这两类对象所有的行为,客户端代码才会透明的使用,无论调用容器对象还是叶子对象,接口方法都是一样的,这就是透明,针对客户端代码的透明。

/// <summary>
/// 该抽象类就是文件夹抽象接口的定义,该类型就相当于是抽象构件Component类型
/// </summary>
public abstract class Folder
{
    public abstract void Add(Folder folder);//增加文件夹或文件
    public abstract void Remove(Folder folder);//删除文件夹或者文件
    public abstract void Open();    //打开文件或者文件夹--该操作相当于Component类型的Operation方法
}

/// <summary>
/// 该Word文档类就是叶子构件的定义,该类型就相当于是Leaf类型,不能在包含子对象
/// </summary>
public sealed class Word : Folder
{
    public override void Add(Folder folder)//增加文件夹或文件
    {
        throw new Exception("Word文档不具有该功能");
    }

    public override void Remove(Folder folder)//删除文件夹或者文件
    {
        throw new Exception("Word文档不具有该功能");
    }

    public override void Open()//打开文件--该操作相当于Component类型的Operation方法
    {
        Console.WriteLine("打开Word文档,开始进行编辑");
    }
}

/// <summary>
/// SonFolder类型就是树枝构件,由于我们使用的是“透明式”,所以Add,Remove都是从Folder类型继承下来的
/// </summary>
public class SonFolder : Folder
{
    public override void Add(Folder folder)//增加文件夹或文件
    {
        Console.WriteLine("文件或者文件夹已经增加成功");
    }

    public override void Remove(Folder folder)//删除文件夹或者文件
    {
        Console.WriteLine("文件或者文件夹已经删除成功");
    }

    public override void Open()//打开文件夹--该操作相当于Component类型的Operation方法
    {
        Console.WriteLine("已经打开当前文件夹");
    }
}

public class Program
{
    static void Main()
    {
        Folder myword = new Word();
        myword.Open();//打开文件,处理文件
        myword.Add(new SonFolder());//抛出异常
        myword.Remove(new SonFolder());//抛出异常

        Folder myfolder = new SonFolder();
        myfolder.Open();//打开文件夹
        myfolder.Add(new SonFolder());//成功增加文件或者文件夹
        myfolder.Remove(new SonFolder());//成功删除文件或者文件夹
    }
}

2、安全式的组合模式

指“抽象构件角色”只定义叶子对象的方法,确切的说这个抽象构件只定义两类对象共有的行为,然后容器对象的方法定义在“树枝构件角色”上,这样叶子对象有叶子对象的方法,容器对象有容器对象的方法,这样责任很明确,当然调用肯定不会抛出异常了。

/// <summary>
/// 该抽象类就是文件夹抽象接口的定义,该类型就相当于是抽象构件Component类型
/// </summary>
public abstract class Folder //该类型少了容器对象管理子对象的方法的定义,换了地方,在树枝构件也就是SonFolder类型
{
    public abstract void Open();  //打开文件或者文件夹--该操作相当于Component类型的Operation方法
}

/// <summary>
/// 该Word文档类就是叶子构件的定义,该类型就相当于是Leaf类型,不能在包含子对象
/// </summary>
public sealed class Word : Folder  //这类型现在很干净
{
    public override void Open() //打开文件--该操作相当于Component类型的Operation方法
    {
        Console.WriteLine("打开Word文档,开始进行编辑");
    }
}

/// <summary>
/// SonFolder类型就是树枝构件,现在由于我们使用的是“安全式”,所以Add,Remove都是从此处开始定义的
/// </summary>
public abstract class SonFolder : Folder //这里可以是抽象接口,可以自己根据自己的情况而定
{
    public abstract void Add(Folder folder); //增加文件夹或文件
    public abstract void Remove(Folder folder); //删除文件夹或者文件
    public override void Open()//打开文件夹--该操作相当于Component类型的Operation方法
    {
        Console.WriteLine("已经打开当前文件夹");
    }
}

/// <summary>
/// NextFolder类型就是树枝构件的实现类
/// </summary>
public sealed class NextFolder : SonFolder
{
    public override void Add(Folder folder)//增加文件夹或文件
    {
        Console.WriteLine("文件或者文件夹已经增加成功");
    }

    public override void Remove(Folder folder) //删除文件夹或者文件
    {
        Console.WriteLine("文件或者文件夹已经删除成功");
    }

    public override void Open()//打开文件夹--该操作相当于Component类型的Operation方法
    {
        Console.WriteLine("已经打开当前文件夹");
    }
}

public class Program
{
    static void Main()
    {
        Folder myword = new Word();//这是安全的组合模式
        myword.Open();//打开文件,处理文件
        Folder myfolder = new NextFolder();
        myfolder.Open();//打开文件夹

        //此处要是用增加和删除功能,需要转型的操作,否则不能使用
        ((SonFolder)myfolder).Add(new NextFolder());//成功增加文件或者文件夹
        ((SonFolder)myfolder).Remove(new NextFolder());//成功删除文件或者文件夹
    }
}

六、组合模式的实现要点:

1、Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
2、将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复杂内部实现结构——发生依赖关系,从而更能“应对变化”。
3、Composite模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.Net控件的实现在这方面为我们提供了一个很好的示范。
4、Composite模式在具体实现中,可以让父对象中的子对象反向追朔;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

组合模式的优点:

(1)、组合模式使得客户端代码可以一致地处理对象和对象容器,无需关系处理的单个对象,还是组合的对象容器。
(2)、将”客户代码与复杂的对象容器结构“解耦。
(3)、可以更容易地往组合对象中加入新的构件。

组合模式的缺点:

使得设计更加复杂。客户端需要花更多时间理清类之间的层次关系。(这个是几乎所有设计模式所面临的问题)。

在以下情况下应该考虑使用组合模式:

(1)、需要表示一个对象整体或部分的层次结构。
(2)、希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

七、.NET 中组合模式的实现

其实组合模式在FCL里面运用还是很多的,不知道大家是不是有所感觉,这个模式大多数是运用在控件上或者是和界面操作、展示相关的操作上。这个模式在.NET 中最典型的应用就是应用与WinForms和Web的开发中,在.NET类库中,都为这两个平台提供了很多现有的控件,然而System.Windows.Forms.dll中System.Windows.Forms.Control类就应用了组合模式,因为控件包括Label、TextBox等这样的简单控件,这些控件可以理解为叶子对象,同时也包括GroupBox、DataGrid这样复合的控件或者叫容器控件,每个控件都需要调用OnPaint方法来进行控件显示,为了表示这种对象之间整体与部分的层次结构,微软把Control类的实现应用了组合模式(确切地说应用了透明式的组合模式)。

到此这篇关于.Net结构型设计模式之组合模式(Composite)的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • .Net结构型设计模式之装饰模式(Decorator)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五 .装饰模式的具体代码实现 六.装饰模式的实现要点: 1.装饰模式的优点: 2.装饰模式的缺点: 3.在以下情况下应当使用桥接模式: 七..NET 中装饰模式的实现 一.动机(Motivate) 在房子装修的过程中,各种功能可以相互组合,来增加房子的功用.类似的,如果我们在软件系统中,要给某个类型或者对象增加功能,如果使用“继承”的方案来写代码,就会出现子类暴涨的情况.比如:IMar

  • .Net结构型设计模式之适配器模式(Adapter)

    一.动机(Motivation) 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的. 如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口? 二.意图(Intent) 将一个类的接口转换成客户希望的另一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 例说Adapter应用 这种实际上是一种委派的调用,本来是发送请求给MyStack,但是M

  • .Net结构型设计模式之桥接模式(Bridge)

    一.动机(Motivation) 在很多游戏场景中,会有这样的情况:[装备]本身会有的自己固有的逻辑,比如枪支,会有型号的问题,同时现在很多的游戏又在不同的介质平台上运行和使用,这样就使得游戏的[装备]具有了两个变化的维度——一个变化的维度为“平台的变化”,另一个变化的维度为“型号的变化”.如果我们要写代码实现这款游戏,难道我们针对每种平台都实现一套独立的[装备]吗?复用在哪里?如何应对这种“多维度的变化”?如何利用面向对象技术来使得[装备]可以轻松地沿着“平台”和“型号”两个方向变化,而不引入

  • .Net结构型设计模式之组合模式(Composite)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.组合模式的具体代码实现 1.透明式的组合模式 2.安全式的组合模式 六.组合模式的实现要点: 组合模式的优点: 组合模式的缺点: 在以下情况下应该考虑使用组合模式: 七..NET 中组合模式的实现 一.动机(Motivate) 在我们的操作系统中有文件夹的概念,文件夹可以包含文件夹,可以嵌套多层,最里面包含的是文件,这个概念和“俄罗斯套娃”很像.当然还有很多的例子,例如我们使用系统

  • Java结构型设计模式之组合模式详解

    目录 组合模式 应用场景 优缺点 主要角色 组合模式结构 分类 透明组合模式 创建抽象根节点 创建树枝节点 创建叶子节点 客户端调用 安全组合模式 创建抽象根节点 创建树枝节点 创建叶子节点 客户端调用 组合模式 组合模式(Composite Pattern)也称为整体-部分(Part-Whole)模式,属于结构型模式. 它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户端对单个对象和组合对象的使用具有一致性. 组合模式一般用来描述整体与部分的关系,它将对象

  • .Net结构型设计模式之代理模式(Proxy)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.代理模式的分类: 六.代理模式的具体实现 七.代理模式的实现要点: 1.代理模式的优点: 2.代理模式的缺点: 3.代理模式的使用场景: 八..NET 中代理模式的实现 九.总结 一.动机(Motivate) 在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者.或者系统结构带来很多麻烦.如何在不失去

  • Java结构型设计模式中建造者模式示例详解

    目录 建造者模式 概述 角色 优缺点 应用场景 基本使用 创建产品类 创建建造者类 使用 链式写法 创建产品类与建造者类 使用 建造者模式 概述 建造者模式(Builder Pattern)属于创建型模式. 它是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示. 简而言之:建造者模式就是使用多个简单的对象一步一步构建成一个复杂的对象. 建造者模式适用于创建对象需要很多步骤,但是步骤的顺序不一定固定.如果一个对象有非常复杂的内部结构(很多属性),可以将复杂对象的创建和使用进行分

  • Java结构型设计模式之桥接模式详细讲解

    目录 桥接模式 概述 应用场景 优缺点 主要角色 桥接模式的基本使用 创建实现角色 创建具体实现角色 创建抽象角色 创建修正抽象角色 客户端调用 桥接模式实现消息发送 创建实现角色 创建具体实现角色 创建抽象角色 创建修正抽象角色 客户端调用 桥接模式 概述 桥接模式(Bridge Pattern)也称为桥梁模式.接口(Interfce)模式或柄体(Handle and Body)模式,属于结构型模式. 它是将抽象部分与它的具体实现部分分离,使它们都可以独立地变化. 桥接模式主要目的是通过组合的

  • .Net结构型设计模式之外观模式(Facade)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.外观模式的具体实现 六.实现要点: 1.外观模式的优点: 2.外观模式的缺点: 3.在以下情况下可以考虑使用外观模式: 七.NET 中外观模式的实现 一.动机(Motivate) 在软件系统开发的过程中,当组件的客户(即外部接口,或客户程序)和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战.如何简化外部客户程序和系统间的交互

  • Java结构型设计模式中代理模式示例详解

    目录 代理模式 分类 主要角色 作用 静态代理与动态代理的区别 静态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 JDK动态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 小优化 CGLIB动态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 小优化 CGLIB与JDK动态代理区别 1.执行条件 2.实现机制 3.性能 代理模式 代理模式(Proxy Pattern)属于结构型模式. 它是指为其他对象提供一种代理以控制对这个对象的

  • java设计模式之组合模式(Composite)

    概述 是一种结构型模式,将对象以树形结构组织起来,以表示"部分 - 整体"的层次结构,使得客户端对单个对象和组合对象的使用具有唯一性. UML类图 上面的类图包含的角色: Component:为参加组合的对象声明一个公共的接口,不管是组合还是叶节点. Leaf:在组合中表示叶子结点对象,叶子结点没有子结点. Composite:表示参加组合的有子对象的对象,并给出树枝构建的行为: 代码示例 import java.util.ArrayList; import java.util.Lis

  • C++设计模式之组合模式(Composite)

    组合模式为了描述分支包含关系,也就是我们说的树形关系,其对象分为枝和叶,每一枝可包含枝和叶,直到全部为叶节点.我们对枝和叶进行行为抽象,可认为枝和叶都是Component,而叶是最小的操作单元,其下不存在枝和叶,而枝作为Composite里面存有其下枝和叶的组件列表. 作用 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性 类视图 实现 #include <iostream> #include <list> #in

随机推荐