C#中的composite模式示例详解

C#中的Composite模式示例详解

Composite模式是一种结构型设计模式,它可以通过组合多个对象来创建一个复杂的结构,并且与它们的父对象一起使用。这种模式可以让客户端代码以统一的方式来处理单个对象和对象组合的结构,而不需要区分它们之间的差异,从而提高了代码的可维护性和可扩展性。接下来,我们将通过两个示例来详细讲解C#中的Composite模式。

示例一:图形绘制

假设我们要编写一个图形绘制程序,能够绘制基本的图形(如圆形、矩形、三角形等)以及复杂的组合图形(如多个基本图形组成的图形)。在这种情况下,我们可以使用Composite模式来实现。具体实现步骤如下:

步骤一:定义图形接口

我们首先需要定义一个表示图形的接口IShape,它包含了绘制图形的方法Draw()和计算面积的方法GetArea()

public interface IShape
{
    void Draw();
    double GetArea();
}

步骤二:实现基本图形类

我们实现三个基本图形类:圆形、矩形和三角形,它们都实现了IShape接口。

public class Circle : IShape
{
    public void Draw()
    {
        Console.WriteLine("Draw Circle");
    }

    public double GetArea()
    {
        return Math.PI * r * r;
    }

    private double r;
}

public class Rectangle : IShape
{
    public void Draw()
    {
        Console.WriteLine("Draw Rectangle");
    }

    public double GetArea()
    {
        return w * h;
    }

    private double w;
    private double h;
}

public class Triangle : IShape
{
    public void Draw()
    {
        Console.WriteLine("Draw Triangle");
    }

    public double GetArea()
    {
        return 0.5 * b * h;
    }

    private double b;
    private double h;
}

步骤三:实现图形组合类

我们定义一个表示图形组合的类CompositeShape,它包含了一个IShape类型的列表,在Draw()方法中遍历每个图形的绘制操作,在GetArea()方法中遍历每个图形的面积计算操作并累加结果。

public class CompositeShape : IShape
{
    public CompositeShape(ICollection<IShape> shapes)
    {
        this.shapes = shapes;
    }

    public void Draw()
    {
        foreach (var shape in shapes)
        {
            shape.Draw();
        }
    }

    public double GetArea()
    {
        double area = 0.0;
        foreach (var shape in shapes)
        {
            area += shape.GetArea();
        }
        return area;
    }

    private ICollection<IShape> shapes;
}

步骤四:使用图形绘制程序

我们可以通过如下代码来使用图形绘制程序:

var circle = new Circle();
var rectangle = new Rectangle();
var triangle = new Triangle();

var compositeShape = new CompositeShape(new IShape[] { circle, rectangle, triangle });
var area = compositeShape.GetArea();
Console.WriteLine($"The area of the composite shape is {area}");

示例二:文件系统管理

假设我们要实现一个文件系统管理程序,能够对文件和文件夹进行增、删、改、查等操作。在这种情况下,我们可以使用Composite模式来实现。具体实现步骤如下:

步骤一:定义文件系统接口

我们首先需要定义一个表示文件系统节点的接口IFileSystemNode,它包含了常用的增、删、改、查等方法。

public interface IFileSystemNode
{
    string Name { get; }
    bool IsDirectory { get; }
    void Add(IFileSystemNode node);
    void Remove(IFileSystemNode node);
    IFileSystemNode GetChildByName(string name);
}

步骤二:实现文件和文件夹类

我们实现两个类:文件和文件夹,它们都实现了IFileSystemNode接口。文件类只包含了文件名,而文件夹类还包含了一个子文件系统节点列表。

public class File : IFileSystemNode
{
    public File(string name)
    {
        this.name = name;
    }

    public string Name
    {
        get { return name; }
    }

    public bool IsDirectory
    {
        get { return false; }
    }

    public void Add(IFileSystemNode node)
    {
        throw new InvalidOperationException("Cannot add child node to file.");
    }

    public void Remove(IFileSystemNode node)
    {
        throw new InvalidOperationException("Cannot remove child node from file.");
    }

    public IFileSystemNode GetChildByName(string name)
    {
        throw new InvalidOperationException("Cannot get child node of file.");
    }

    private string name;
}

public class Directory : IFileSystemNode
{
    public Directory(string name)
    {
        this.name = name;
        this.children = new List<IFileSystemNode>();
    }

    public string Name
    {
        get { return name; }
    }

    public bool IsDirectory
    {
        get { return true; }
    }

    public void Add(IFileSystemNode node)
    {
        children.Add(node);
    }

    public void Remove(IFileSystemNode node)
    {
        children.Remove(node);
    }

    public IFileSystemNode GetChildByName(string name)
    {
        return children.FirstOrDefault(c => c.Name == name);
    }

    private string name;
    private List<IFileSystemNode> children;
}

步骤三:实现文件系统管理器

我们定义一个表示文件系统管理器的类FileSystemManager,它包含了一个根节点以及常用的增、删、改、查等方法,还包含了一个深度优先遍历方法Traverse(),可以遍历整个文件系统。

public class FileSystemManager
{
    public FileSystemManager()
    {
        root = new Directory("Root");
    }

    public void Add(IFileSystemNode node, string path)
    {
        var parent = GetParentNode(path);
        if (!parent.IsDirectory)
        {
            throw new InvalidOperationException("Cannot add child node to file.");
        }
        parent.Add(node);
    }

    public void Remove(string path)
    {
        var (parent, name) = GetParentNodeAndName(path);
        if (!parent.IsDirectory)
        {
            throw new InvalidOperationException("Cannot remove child node from file.");
        }
        var child = parent.GetChildByName(name);
        if (child == null)
        {
            throw new InvalidOperationException("Node not found.");
        }
        parent.Remove(child);
    }

    public void Rename(string path, string newName)
    {
        var (parent, name) = GetParentNodeAndName(path);
        if (!parent.IsDirectory)
        {
            throw new InvalidOperationException("Cannot rename file.");
        }
        var child = parent.GetChildByName(name);
        if (child == null)
        {
            throw new InvalidOperationException("Node not found.");
        }
        parent.Remove(child);
        child = child.IsDirectory
            ? new Directory(newName)
            : new File(newName);
        parent.Add(child, path.Substring(0, path.Length - name.Length) + newName);
    }

    public IFileSystemNode GetNode(string path)
    {
        var (parent, name) = GetParentNodeAndName(path);
        if (parent.IsDirectory)
        {
            return parent.GetChildByName(name);
        }
        else if (parent != null && parent.Name == name)
        {
            return parent;
        }
        else
        {
            throw new InvalidOperationException("Node not found.");
        }
    }

    public void Traverse(Action<IFileSystemNode> action)
    {
        Traverse(root, action);
    }

    private void Traverse(IFileSystemNode node, Action<IFileSystemNode> action)
    {
        action(node);
        if (node.IsDirectory)
        {
            foreach (var child in (node as Directory).GetChildren())
            {
                Traverse(child, action);
            }
        }
    }

    private IFileSystemNode GetParentNode(string path)
    {
        var parts = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length == 1 && parts[0] == root.Name)
        {
            return root;
        }
        var parent = root.GetChildByName(parts[0]);
        if (parent == null)
        {
            throw new InvalidOperationException("Invalid path.");
        }
        for (int i = 1; i < parts.Length - 1; i++)
        {
            var child = parent.GetChildByName(parts[i]);
            if (child == null || !child.IsDirectory)
            {
                throw new InvalidOperationException("Invalid path.");
            }
            parent = child;
        }
        return parent;
    }

    private (IFileSystemNode, string) GetParentNodeAndName(string path)
    {
        var parts = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length == 1 && parts[0] == root.Name)
        {
            return (null, "");
        }
        var parent = root.GetChildByName(parts[0]);
        if (parent == null)
        {
            throw new InvalidOperationException("Invalid path.");
        }
        for (int i = 1; i < parts.Length - 1; i++)
        {
            var child = parent.GetChildByName(parts[i]);
            if (child == null || !child.IsDirectory)
            {
                throw new InvalidOperationException("Invalid path.");
            }
            parent = child;
        }
        return (parent, parts[parts.Length - 1]);
    }

    private IFileSystemNode root;
}

步骤四:使用文件系统管理器

我们可以通过如下代码来使用文件系统管理器:

var manager = new FileSystemManager();

var rootDir = manager.GetNode("/Root");
var docsDir = new Directory("Documents");
manager.Add(docsDir, "/Root");
var readmeFile = new File("README.md");
manager.Add(readmeFile, "/Root/Documents/");
var todoFile = new File("TODO.md");
manager.Add(todoFile, "/Root/Documents/");
manager.Rename("/Root/Documents/TODO.md", "TASK.md");

manager.Traverse(node =>
{
    Console.WriteLine($"{(node.IsDirectory ? "Directory" : "File")} {node.Name}");
});

以上就是两个C#中实现Composite模式的示例,希望能对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#中的composite模式示例详解 - Python技术站

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

相关文章

  • c#中利用委托反射将DataTable转换为实体集的代码

    下面是详细的“c#中利用委托反射将DataTable转换为实体集的代码”的攻略: 1. 委托与反射简介 委托是C#中非常重要的一个概念,它可以理解为一种能够存储指向方法的变量,可以通过委托调用方法。而反射则是C#中的一个高级特性,可以在程序运行时动态地获取和调用对象的类型、方法、属性等信息。 2. 实现步骤 实现将DataTable转换为实体集的代码,需要经…

    C# 2023年5月31日
    00
  • .net C# 实现任意List的笛卡尔乘积算法代码

    以下是“.net C# 实现任意List的笛卡尔乘积算法代码”的完整攻略。 什么是笛卡尔积? 笛卡尔积,又称交叉积、叉积,是指对两个集合进行操作,其中一个集合中每一个元素都与另一个集合中的所有元素一一组合,生成一个新的集合。例如,集合 A={a,b},集合 B={0,1,2},A 和 B 的笛卡尔积是 {(a,0),(a,1),(a,2),(b,0),(b,…

    C# 2023年6月1日
    00
  • .NET 2.0 的压缩功能代码

    .NET 2.0 提供了压缩和解压缩文件的功能,主要是通过System.IO.Compression和System.IO.Compression.FileSystem命名空间下的类型来实现。 以下是压缩文件的示例代码: using System.IO.Compression; public static void CompressFile(string so…

    C# 2023年5月31日
    00
  • Asp.net操作Excel更轻松的实现代码

    Asp.net操作Excel更轻松的实现代码 在Asp.net中,操作Excel文件的需求比较常见,而通过使用第三方库和相关命名空间中的类,可以更轻松地实现对Excel文件的读取和写入操作。 第一步:安装Nuget包 我们需要安装一个Nuget包来实现对Excel的操作,这个Nuget包就是EPPlus,它是一个免费的开源项目,支持2007和2010版本的E…

    C# 2023年5月31日
    00
  • asp.net 动态引用样式表代码

    下面是详细讲解“asp.net 动态引用样式表代码”的攻略。 1. 什么是动态引用样式表代码 ASP.NET 动态引用样式表代码指的是在 ASP.NET 网页中,通过使用响应式设计原理,利用 C# 或 VB 语言实现样式表的动态引用,使得页面实现了样式与内容分离的效果,提高了网页的可维护性。 2. 如何使用 ASP.NET 动态引用样式表代码 常见的动态引用…

    C# 2023年5月31日
    00
  • C# 实例解释面向对象编程中的单一功能原则(示例代码)

    针对您的问题,以下是C# 实例解释面向对象编程中的单一功能原则的攻略及示例代码。 单一功能原则 单一功能原则(Single Responsibility Principle,SRP)是面向对象编程中的一项核心原则,其核心思想是一个类或模块只负责一项职责(也就是只有一个引起它变化的原因)。这样可以让代码更加易于维护、修改和测试。举例来说,如果一个类负责多项职责…

    C# 2023年6月1日
    00
  • asp.net页面master页面与ascx用户控件传值的问题

    ASP.NET页面中,Master页面和ASCX用户控件是常见的组件。Master页面通常用于定义网站的整体布局和风格,而ASCX用户控件则用于封装重复使用的控件或作为嵌入其他页面的组件。在一些复杂的应用场景中,我们需要在Master页面和ASCX用户控件之间传递数据或状态,下面是传值的两种方法。 方法一:通过属性(Property)传值 借助于Proper…

    C# 2023年6月3日
    00
  • ASP.NET Core程序发布到Linux生产环境详解

    ASP.NET Core程序发布到Linux生产环境详解 在本攻略中,我们将详细介绍如何将ASP.NET Core程序发布到Linux生产环境中。我们将介绍两种不同的发布方式,并提供两个示例说明。 准备工作 在将ASP.NET Core程序发布到Linux生产环境之前,需要进行以下准备工作: 安装Linux操作系统。 安装.Net Core运行时。 安装Ng…

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