C# 设计模式系列教程-组合模式

下面我将详细讲解“C# 设计模式系列教程-组合模式”的完整攻略。

什么是组合模式

组合模式是一种结构型设计模式,旨在将多个对象合成树形结构以表示具有“整体-部分”关系的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

其中,组合模式将“组合对象”和“叶子对象”抽象为一个共同的接口。这样,用户就可以统一地操作组合对象和叶子对象。

组合模式的应用场景

组合模式通常用于树形结构中。在这种情况下,单个对象和组合对象具有相同的基类或接口,并且可以嵌套使用。

下面是一些应用组合模式的经典场景:

  • Windows 中的 UI 界面就是以树形结构组织的。
  • 目录和文件系统可以使用组合模式实现。
  • 人员和部门管理的组织结构图也可以使用组合模式实现。

组合模式的结构和角色

组合模式由以下角色构成:

  • 抽象组件(Component):它是一个抽象类或接口,定义了组合对象的通用行为,包括添加、删除、获取子对象等操作。
  • 叶子节点(Leaf):它表示组合中的叶子对象,它没有子对象。
  • 组合节点(Composite):它表示组合中的组合对象,具有子对象。
  • 客户端(Client):通过使用组合模式,可以统一对组合对象和叶子对象进行操作,客户端可以针对抽象组件编程,无需知道具体的实现类。

下面是组合模式的 UML 结构图:

                               +------+  +------+
                               |Component |  | Component |
                               +------+  +------+
                                  / \        /    \
                                 /   \      /      \
                        +-------+   +--------+   +-------+
                        | Leaf  |   | Composite |   | Leaf |
                        +-------+   +----------+  +-------+

组合模式的示例代码

下面是一些使用组合模式的示例代码。

示例1:使用组合模式实现菜单

using System;
using System.Collections.Generic;

namespace CompositePattern
{
    // 抽象组件:菜单项
    public abstract class MenuItem
    {
        public string Name { get; set; }
        public MenuItem(string name) => Name = name;
        public abstract void AddChild(MenuItem child);
        public abstract void RemoveChild(MenuItem child);
        public abstract void Render(int depth);
    }

    // 叶子节点:菜单项
    public class MenuItemLeaf : MenuItem
    {
        public MenuItemLeaf(string name) : base(name) { }
        public override void AddChild(MenuItem child) => Console.WriteLine("叶子节点不能添加子节点");
        public override void RemoveChild(MenuItem child) => Console.WriteLine("叶子节点没有子节点可以删除");
        public override void Render(int depth)
        {
            Console.WriteLine(new string('-', depth) + Name);
        }
    }

    // 组合节点:菜单项
    public class MenuItemComposite : MenuItem
    {
        private List<MenuItem> children = new List<MenuItem>();
        public MenuItemComposite(string name) : base(name) { }
        public override void AddChild(MenuItem child) => children.Add(child);
        public override void RemoveChild(MenuItem child) => children.Remove(child);
        public override void Render(int depth)
        {
            Console.WriteLine(new string('-', depth) + Name);
            foreach (var child in children)
                child.Render(depth + 2);
        }
    }

    // 客户端:菜单
    public class Menu
    {
        public MenuItem MainItem = new MenuItemComposite("主菜单");
        public void Render()
        {
            MainItem.Render(0);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var menu = new Menu();

            var fileMenu = new MenuItemComposite("文件菜单");
            fileMenu.AddChild(new MenuItemLeaf("新建"));
            fileMenu.AddChild(new MenuItemLeaf("打开"));
            fileMenu.AddChild(new MenuItemLeaf("保存"));

            var editMenu = new MenuItemComposite("编辑菜单");
            editMenu.AddChild(new MenuItemLeaf("复制"));
            editMenu.AddChild(new MenuItemLeaf("粘贴"));
            editMenu.AddChild(new MenuItemLeaf("剪切"));

            menu.MainItem.AddChild(fileMenu);
            menu.MainItem.AddChild(editMenu);

            menu.Render();
        }
    }
}

示例2:使用组合模式实现文件系统

using System;
using System.Collections.Generic;

namespace CompositePattern
{
    // 抽象组件:文件系统的元素
    public abstract class FileSystemElement
    {
        public string Name { get; set; }
        public FileSystemElement(string name) => Name = name;
        public abstract void AddChild(FileSystemElement child);
        public abstract void RemoveChild(FileSystemElement child);
        public abstract void Render(int depth);
    }

    // 叶子节点:文件
    public class File : FileSystemElement
    {
        public File(string name) : base(name) { }
        public override void AddChild(FileSystemElement child) => Console.WriteLine("文件不能添加子元素");
        public override void RemoveChild(FileSystemElement child) => Console.WriteLine("文件没有子元素可以删除");
        public override void Render(int depth)
        {
            Console.WriteLine(new string('-', depth) + Name);
        }
    }

    // 组合节点:文件夹
    public class Folder : FileSystemElement
    {
        private List<FileSystemElement> children = new List<FileSystemElement>();
        public Folder(string name) : base(name) { }
        public override void AddChild(FileSystemElement child) => children.Add(child);
        public override void RemoveChild(FileSystemElement child) => children.Remove(child);
        public override void Render(int depth)
        {
            Console.WriteLine(new string('-', depth) + Name + "/");
            foreach (var child in children)
                child.Render(depth + 2);
        }
    }

    // 客户端:文件系统
    public class FileSystem
    {
        public FileSystemElement Root = new Folder("根目录");
        public void Render()
        {
            Root.Render(0);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var fileSystem = new FileSystem();

            var systemFolder = new Folder("系统文件夹");
            systemFolder.AddChild(new File("Windows.exe"));
            systemFolder.AddChild(new File("cmd.exe"));

            var dataFolder = new Folder("数据文件夹");
            dataFolder.AddChild(new File("data.txt"));
            dataFolder.AddChild(new File("user.db"));

            var userFolder = new Folder("用户文件夹");
            userFolder.AddChild(new Folder("文档"));
            userFolder.AddChild(new Folder("下载"));
            userFolder.AddChild(new Folder("视频"));

            userFolder.GetChild<Folder>("文档").AddChild(new File("readme.txt"));
            userFolder.GetChild<Folder>("文档").AddChild(new File("book.pdf"));

            fileSystem.Root.AddChild(systemFolder);
            fileSystem.Root.AddChild(dataFolder);
            fileSystem.Root.AddChild(userFolder);

            fileSystem.Render();
        }
    }

    public static class FileSystemElementExtensions
    {
        public static T GetChild<T>(this Folder folder, string name) where T : FileSystemElement
        {
            foreach (var element in folder.children)
                if (element.GetType() == typeof(T) && element.Name == name)
                    return (T)element;
            return null;
        }
    }
}

总结

组合模式是一种非常实用的设计模式,可用于树形结构的场景。通过使用组合模式,可以将单个对象和组合对象统一起来,用户操作时也更加方便。

在使用组合模式时,需要注意抽象组件的设计,它应该定义操作组合对象和叶子对象的通用接口。另外,组合节点应该维护一个子对象的列表,以便对子对象进行管理。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C# 设计模式系列教程-组合模式 - Python技术站

(0)
上一篇 2023年5月31日
下一篇 2023年5月31日

相关文章

  • c#完美截断字符串代码(中文+非中文)

    C#完美截断字符串代码(中文+非中文)攻略 概述 在C#中,对一个字符串进行截断,即取其中一部分,是一个比较常见的操作。本文将介绍一种完美的字符串截断方法,支持中文和非中文的字符串。 方法 string SubstringSmart(string str, int length) { if (string.IsNullOrEmpty(str)) { retu…

    C# 2023年5月31日
    00
  • Jquery上传插件 uploadify v3.1使用说明

    简介 uploadify是一个基于jQuery的多文件异步上传插件,可以提供灵活的文件上传功能。本文将详细介绍uploadify的使用方法和基本配置。 下载和引入 首先,需要下载uploadify插件,可以在官方网站http://www.uploadify.com/下载。下载后将js、css和swf文件放入相应目录,并在HTML文件中引入。 <link…

    C# 2023年5月31日
    00
  • ASP.NET实现将word文档转换成pdf的方法

    安装Microsoft Office Interop组件 在ASP.NET中将word文档转换为PDF,需要使用Microsoft Office Interop组件。在安装组件之前,需要先安装Microsoft Office软件。之后在Visual Studio中通过NuGet安装Microsoft.Office.Interop.Word组件。安装完成后,将…

    C# 2023年6月1日
    00
  • C#中的委托介绍

    C#中的委托(Delegate)是一种特殊的类,用来实现事件机制、回调函数和多播委托等功能。它可以看作是一个函数的引用,可以将方法作为参数传递给委托,从而达到动态的、可扩展的编程效果。 委托的定义 C#中定义委托需要使用关键字delegate,并且需要指定方法的参数列表和返回值类型。例如: delegate int DelegateFunc(int x, i…

    C# 2023年6月7日
    00
  • Unity实现图片水印生成

    下面就来详细讲解如何实现“Unity实现图片水印生成”的完整攻略。 需求分析 在实现图片水印生成之前,我们需要先对需求进行分析: 将水印添加到图片上 水印可配置:水印文字内容、字体、大小、颜色、位置、透明度等 输出带水印的图片 实现步骤 1. 下载字体文件 首先我们需要下载所需的字体文件。可以在字体网站上寻找并不断尝试,也可以在自己电脑上的字体目录中找到。 …

    C# 2023年6月3日
    00
  • HttpWebRequest出错.Section=ResponseHeader Detail=CR

    标题:解决 HttpWebRequest 出错 Section=ResponseHeader,Detail=CR 的攻略 可能出现的错误信息: 当使用 HttpWebRequest 请求 Web 服务器端数据时,有可能会出现 Section=ResponseHeader,Detail=CR 的错误提示,该错误提示可能是由于某些特殊字符在服务器端返回的响应中出…

    C# 2023年5月14日
    00
  • C#中字符串的一般性和特殊性

    C#中字符串的一般性和特殊性 如果你正在学习C#,字符串(string)是一个基础重要的数据类型。在本文中,我们将介绍C#中字符串的一般性和特殊性,以及在实际编程中如何使用它们。 C#中字符串的一般性 字符串的定义 在C#中定义字符串变量的语法格式为: string variableName; 其中,variableName为字符串变量的名称。可以使用赋值运…

    C# 2023年6月8日
    00
  • XUnit数据共享与并行测试

    引言 在单元或者集成测试的过程中,需要测试的用例非常多,如果测试是一条一条过,那么需要花费不少的时间。从 V2 开始,默认情况下 XUnit 自动配置并行(参考资料),大大提升了测试速度。本文将对 ASP.NET CORE WEBAPI 程序进行集成测试,并探讨 XUnit 的数据共享与测试并行的方法。 XUnit默认在一个类内的测试代码是串行执行的,而在不…

    C# 2023年5月10日
    00
合作推广
合作推广
分享本页
返回顶部