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日

相关文章

  • ASP.NET Core MVC 从入门到精通之HttpContext

    随着技术的发展,ASP.NET Core MVC也推出了好长时间,经过不断的版本更新迭代,已经越来越完善,本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容,适用于初学者,在校毕业生,或其他想从事ASP.NET Core MVC 系统开发的人员。 经过前几篇文章的讲解,初步了解ASP.NET Core MVC项目创建,启…

    C# 2023年5月4日
    00
  • c#使用正则表达式匹配字符串验证URL示例

    下面是详细讲解c#使用正则表达式匹配字符串验证URL的完整攻略。 什么是正则表达式 正则表达式是一种用于匹配文本的工具,它可以用来查找、替换或者检测文本中符合特定格式的字符串。 在c#中,.NET框架提供了使用正则表达式的类库,通过这些类库可以方便的进行字符串匹配的操作。 如何使用正则表达式匹配URL 在c#中,我们可以使用Match类提供的方法来进行正则表…

    C# 2023年6月8日
    00
  • asp.net 使用ObjectDataSource控件在ASP.NET中实现Ajax真分页

    实现Ajax真分页的方法有很多种,其中一种是使用ObjectDataSource控件。以下是实现该功能的步骤: 步骤1:创建一个ASP.NET Web表单 首先需要创建一个ASP.NET Web表单,并在该表单中添加一个GridView控件用于显示数据。 步骤2:使用ObjectDataSource控件绑定数据 使用ObjectDataSource控件可以方…

    C# 2023年6月3日
    00
  • c#3.0实现延迟赋值示例

    下面我详细讲解一下“C#3.0实现延迟赋值示例”的完整攻略。 什么是延迟赋值 延迟赋值就是指将变量的赋值操作推迟到该变量首次被访问时再执行。这种赋值方式可以有效地减少不必要的计算和内存开销,提高程序的运行效率。 C#3.0如何实现延迟赋值 在C#3.0中,可以使用lazy关键字和Lazy<T>类来实现延迟赋值。 当声明一个变量时,可以在变量前加上…

    C# 2023年6月1日
    00
  • C# 6.0的属性(Property)的语法与初始值详解

    下面是关于“C# 6.0的属性(Property)的语法与初始值详解”的完整攻略。 什么是属性? 在面向对象的语言中,属性通常指的是类的特征或者是状态等方面,可以通过getter和setter方法来访问和修改。 在C#中,属性是一种特殊的方法,用于封装类的状态或者特征,并且具有相应的访问控制。属性通常用于访问私有成员变量并提供对这些成员变量的读取和写入功能。…

    C# 2023年5月31日
    00
  • 常用C#关键字详解教程(比较全面)

    常用C#关键字详解教程 C#是一种现代的、对象导向的编程语言,用于.NET平台。在C#中有许多关键字,这些关键字用于定义变量、控制流程、类或结构、枚举等。下面我们将详细讲解C#中常用的关键字。 变量定义相关关键字 var var用于在编译期间推断变量类型。它指示编译器基于初始化表达式计算左侧变量的类型。例如: var str = "hello wo…

    C# 2023年5月15日
    00
  • C# Stream.Seek – 在流中定位

    Stream.Seek 方法用于在流中寻找具有给定偏移量的位置,并将流的读/写指针移动到该位置。Seek 方法可用于在文件中进行定位,以便读取或写入指定位置的数据。 使用方法 方法签名 public virtual long Seek(long offset, SeekOrigin origin); 参数含义 offset:偏移量。它表示要在流内移动的字节数…

    C# 2023年4月19日
    00
  • C# 字节数组、各进制字符串数据互转

    前言 上位机开发过程中,格式转换是必不可少的重要环节,经常是十进制转十六进制、十六进制转byte数组又转换回来来回转换,最然进制转换很基础同时 C# 也提供了很多直接方便进行格式转换的方法,但是封装一个工具类会方便很多,不用每次都手写代码逻辑,之前一直都是简单的自己写,稍复杂的就用前辈写好的直接调用,这次自己写一个。 简单的定义为一行代码完事,多一行都不写?…

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