当前位置: 代码网 > it编程>编程语言>Asp.net > .Net结构型设计模式之组合模式(Composite)

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

2024年05月15日 Asp.net 我要评论
一、动机(motivate)在我们的操作系统中有文件夹的概念,文件夹可以包含文件夹,可以嵌套多层,最里面包含的是文件,这个概念和“俄罗斯套娃”很像。当然还有很多的例子,例如我们

一、动机(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)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com