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# wpf解决Popup弹出位置异常问题解决

    在WPF中,我们可以通过Popup弹出一个窗口,但是由于窗口的弹出位置可能会和我们期望的不一样,而且有时候在某些屏幕分辨率下表现得更为明显。在这种情况下,我们需要进行调整以确保Popup窗口出现在我们期望的位置。下面是解决这个问题的方法: 1. 设置PlacementMode属性 在Popup控件中,可以通过设置PlacementMode属性来控制Popup…

    C# 2023年6月6日
    00
  • c# 理解csredis库实现分布式锁的详细流程

    下面是关于实现分布式锁的详细攻略: 1. 简介 在分布式系统中,分布式锁是实现数据安全访问的一种重要手段。常见的分布式锁实现方法有使用Redis实现,在C#中可以使用csredis库来方便地实现分布式锁。 csredis是一个Redis的C#客户端,提供了简单、高性能、高可靠性的封装。在csredis中实现分布式锁需要使用到Redis的原子命令setnx(S…

    C# 2023年6月3日
    00
  • c#操作iis根目录的方法

    C#操作IIS根目录的方法攻略 在使用C#开发Web应用程序时,我们常常需要对IIS服务器中的根目录进行操作。下面介绍一些常见的方法,帮助你轻松管理IIS服务器中的根目录。 1. 使用IIS管理脚本(IIS Management Scripts) IIS管理脚本提供了一系列用于管理IIS服务器的命令行工具。从Windows 7开始,这些工具都自带了。 对于I…

    C# 2023年6月1日
    00
  • Jenkins自动部署Net Core过程图解

    Jenkins自动部署Net Core过程图解 Jenkins是一个流行的开源持续集成和持续交付工具,它可以自动化构建、测试和部署软件。在本文中,我们将介绍如何使用Jenkins自动部署.Net Core应用程序。 准备工作 在开始之前,我们需要完成以下准备工作: 安装Jenkins服务器。 安装.Net Core SDK。 在Jenkins服务器上安装.N…

    C# 2023年5月16日
    00
  • 轻松学习C#的属性

    当您学习C#编程语言时,属性是一个重要的概念。属性可用于对类中的字段进行访问、设置和检查。通过使用属性,可以更好地组织代码并提高代码重用性。 什么是属性? 属性是一种C#编程语言中的特殊语法,它允许使用getter和setter方法对类中的字段进行访问、设置和检查。通过属性,可以在类外部访问私有字段,其本质上是对字段进行封装,确保对数据的访问是安全和可控的。…

    C# 2023年6月1日
    00
  • C#用dynamic一行代码实现反射操作

    dynamic简介 dynamic是.NET Framework4.0的新特性。dynamic的出现让C#具有了弱语言类型的特性。编译器在编译的时候不再对类型进行检查,编译时默认dynamic对象支持你想要的任何特性。 dynamic简化反射实现 使用dynamic来简化反射实现是一种比较常见的编程技巧,它可以减少代码的复杂性并提高可读性。下面是一个使用dy…

    C# 2023年4月25日
    00
  • asp.net c# 调用百度pai实现在线翻译,英文转中文

    要实现asp.net c#调用百度AI实现在线翻译,首先需要获取百度翻译API的访问密钥。然后,通过发送HTTP请求到百度翻译API接口,即可获取到翻译结果。 下面是详细的步骤: 1. 获取百度翻译API访问密钥 访问百度智能云官网,登录或者注册账号 在控制台中创建应用,选择“翻译”作为所需服务。 记录下应用的App ID和API Key 2. 编写C#程序…

    C# 2023年5月31日
    00
  • C#二维数组基本用法实例

    下面是关于“C#二维数组基本用法实例”的完整攻略。 什么是二维数组 在C#中,数组是一个由相同类型的若干元素在连续的存储空间中所组成的集合。而二维数组则是由多个一维数组组合形成的。可以理解为一个数据表格,有行和列两个维度。 声明二维数组 声明二维数组需要指定行数和列数,并使用两个方括号“[]”来表示。语法如下: int[,] numbers = new in…

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