深入理解Java设计模式之组合模式

 更新时间:2021年11月08日 08:39:54   作者:一指流砂~  
这篇文章主要介绍了JAVA设计模式之组合模式的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下

一、什么是组合模式

定义:将对象以树形结构组织起来,以达成“部分-整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。

动机(Motivation)

客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。

如何将“客户代码与复杂的对象容器结构”解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

意图(Intent)

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

组合模式,现在学习就是虚类继承虚类,然后增加虚方法。最终实类继承第二个虚类,重写所有虚方法。

二、组合模式的结构

结构图说明:

(1)Component:组合中的对象声明接口,在适当情况下实现所有类共有的默认行为,声明一个接口用于访问和管理Component的子组件。在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。(可选)

(2)Leaf:在组合中表示叶节点,叶节点没有子节点,定义对象的基本行为。

(3)Composite:定义有子部件的那些部件的行为,存储子部件并在Component接口实现与子部件有关的操作。

(4)Client:通过Component接口操作组合部件的对象。

三、组合模式的使用场景

1.需求重要体现部分与整体的层次结构时

2.你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

四、组合模式的优缺点

优点

1.使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关系自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。

2.更容易在组合体内加入对象部件. 客户端不必因为加入了新的对象部件而更改代码。

缺点

组合模式不容易限制组合中的构件。

五、组合模式的实现

组合模式有两种实现方式,一种是:透明式的组合模式,另外一种是:安全式的组合模式。在这里我就详细说一下何为“透明式”,何为“安全式”。所谓透明式是指“抽象构件角色”定义的接口行为集合包含两个部分,一部分是叶子对象本身所包含的行为(比如Operation),另外一部分是容器对象本身所包含的管理子对象的行为(Add,Remove)。这个抽象构件必须同时包含这两类对象所有的行为,客户端代码才会透明的使用,无论调用容器对象还是叶子对象,接口方法都是一样的,这就是透明,针对客户端代码的透明,但是也有他自己的问题,叶子对象不会包含自己的子对象,为什么要有Add,Remove等类似方法呢,调用叶子对象这样的方法可能(注意:我这里说的是可能,因为有些人会把这些方法实现为空,不做任何动作,当然也不会有异常抛出了,不要抬杠)会抛出异常,这样就不安全了,然后人们就提出了“安全式的组合模式”。所谓安全式是指“抽象构件角色”只定义叶子对象的方法,确切的说这个抽象构件只定义两类对象共有的行为,然后容器对象的方法定义在“树枝构件角色”上,这样叶子对象有叶子对象的方法,容器对象有容器对象的方法,这样责任很明确,当然调用肯定不会抛出异常了。大家可以根据自己的情况自行选择是实现为“透视式”还是“安全式”的,以下我们会针对这两种情况都有实现,具体实现如下:

namespace 透明式的组合模式的实现
{
    /// <summary>
    /// 该抽象类就是文件夹抽象接口的定义,该类型就相当于是抽象构件Component类型
    /// </summary>
    public abstract class Folder
    {
        //增加文件夹或文件
        public abstract void Add(Folder folder);
        //删除文件夹或者文件
        public abstract void Remove(Folder folder);
        //打开文件或者文件夹--该操作相当于Component类型的Operation方法
        public abstract void Open();
    }
    /// <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文档不具有该功能");
        }
        //打开文件--该操作相当于Component类型的Operation方法
        public override void Open()
        {
            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("文件或者文件夹已经删除成功");
        }
        //打开文件夹--该操作相当于Component类型的Operation方法
        public override void Open()
        {
            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());//成功删除文件或者文件夹
            Console.Read();
        }
    }
}

以上代码就是“透明式的组合模式”实现,以下代码就是“安全式的组合模式”实现:

namespace 安全式的组合模式的实现
{
    /// <summary>
    /// 该抽象类就是文件夹抽象接口的定义,该类型就相当于是抽象构件Component类型
    /// </summary>
    public abstract class Folder //该类型少了容器对象管理子对象的方法的定义,换了地方,在树枝构件也就是SonFolder类型
    {
        //打开文件或者文件夹--该操作相当于Component类型的Operation方法
        public abstract void Open();
    }
    /// <summary>
    /// 该Word文档类就是叶子构件的定义,该类型就相当于是Leaf类型,不能在包含子对象
    /// </summary>
    public sealed class Word : Folder  //这类型现在很干净
    {
        //打开文件--该操作相当于Component类型的Operation方法
        public override void Open()
        {
            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);
        //打开文件夹--该操作相当于Component类型的Operation方法
        public override void Open()
        {
            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("文件或者文件夹已经删除成功");
        }
        //打开文件夹--该操作相当于Component类型的Operation方法
        public override void Open()
        {
            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());//成功删除文件或者文件夹
            Console.Read();
        }
    }
}

六、组合模式的.NET下应用

ASP.Net中的Panel对象就是一个Composite对象,而Button对象就是Leaf对象。Button和Panel都继承自System.Web.UI.Control类。它实际上是在Panel里面加了一个Controls属性,然后Controls属性是一个集合属性,它有Add和Remove方法。

在ASP.Net中就是这样,每一个控件都有Controls属性,也就是说每个控件都是一种容器控件(除了LiteralControl)。这种方式把我们对安全性的担忧,统统放到容器(即ASP.Net中的Controls)中去处理。

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

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • spring boot中配置hikari连接池属性方式

    spring boot中配置hikari连接池属性方式

    这篇文章主要介绍了spring boot中配置hikari连接池属性方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • JAVA+Struts2获取服务器地址的方法

    JAVA+Struts2获取服务器地址的方法

    这篇文章主要介绍了JAVA+Struts2获取服务器地址的方法,是Struts2的一个简单应用,具有一定的借鉴与参考价值,需要的朋友可以参考下
    2014-11-11
  • java引用jpython的方法示例

    java引用jpython的方法示例

    这篇文章主要介绍了java引用jpython的方法,结合实例形式分析了java引用jpython及相关使用技巧,需要的朋友可以参考下
    2016-11-11
  • SpringBoot实现自定义事件的方法详解

    SpringBoot实现自定义事件的方法详解

    这篇文章将用实例来和大家介绍一下如何在SpringBoot中自定义事件来使用观察者模式。文中的示例代码讲解详细,对我们学习SpringBoot有一定的帮助,需要的可以参考一下
    2022-06-06
  • Java判断绝对值的方法总结

    Java判断绝对值的方法总结

    在本文中作者为大家分享了两种Java判断绝对值的方法,对此有兴趣的朋友可以和小编一起学习下。
    2018-02-02
  • MybatisPlus #{param}和${param}的用法详解

    MybatisPlus #{param}和${param}的用法详解

    这篇文章主要介绍了MybatisPlus #{param}和${param}的用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • SpringBoot lombok(注解@Getter @Setter)详解

    SpringBoot lombok(注解@Getter @Setter)详解

    通过使用Lombok库,SpringBoot应用可以自动化生成常用的方法如setter和getter,显著降低了代码冗余并提高了开发效率,Lombok的@Getter和@Setter注解用于自动生成属性的访问和修改方法,而@Data注解则提供了一个全面的解决方案
    2024-11-11
  • JMeter自定义日志与日志分析的实现

    JMeter自定义日志与日志分析的实现

    JMeter与Java程序一样,会记录事件日志,本文就介绍一下JMeter自定义日志与日志分析的实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • SpringBoot数据层处理方案精讲

    SpringBoot数据层处理方案精讲

    这篇文章主要介绍了SpringBoot数据层技术的解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-10-10
  • Spring boot搭建邮件服务的完整步骤

    Spring boot搭建邮件服务的完整步骤

    这篇文章主要给大家介绍了关于Spring boot搭建邮件服务的完整步骤,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09

最新评论