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技术站