原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需通过构造函数。这种方式可以提高性能,特别是在创建复杂对象时。C# 中可以通过实现 ICloneable 接口或自定义克隆方法来实现原型模式。

案例 1:文档编辑器中的克隆功能

场景描述
在一个文档编辑器中,用户可以创建复杂的文档对象,包括文本、图片、表格等。为了提供“撤销”功能,我们需要保存文档的多个状态。使用原型模式可以高效地克隆文档对象,避免重复创建复杂的对象。
代码实现

using System;
using System.Collections.Generic;

// 文档类,实现 ICloneable 接口
public class Document : ICloneable
{
    public string Text { get; set; }
    public List<string> Images { get; set; }
    public List<string> Tables { get; set; }

    public Document()
    {
        Images = new List<string>();
        Tables = new List<string>();
    }

    // 克隆方法
    public object Clone()
    {
        Document clonedDocument = new Document
        {
            Text = this.Text,
            Images = new List<string>(this.Images),
            Tables = new List<string>(this.Tables)
        };
        return clonedDocument;
    }

    public override string ToString()
    {
        return $"Text: {Text}, Images: {string.Join(", ", Images)}, Tables: {string.Join(", ", Tables)}";
    }
}

// 客户端代码
public class DocumentEditor
{
    private Stack<Document> _history = new Stack<Document>();

    public void CreateDocument(string text, List<string> images, List<string> tables)
    {
        Document document = new Document
        {
            Text = text,
            Images = images,
            Tables = tables
        };
        _history.Push(document);
    }

    public void Undo()
    {
        if (_history.Count > 1)
        {
            _history.Pop();
            Document currentDocument = _history.Peek();
            Console.WriteLine("Undo: " + currentDocument);
        }
        else
        {
            Console.WriteLine("No more actions to undo.");
        }
    }
}

// 使用示例
public class Program
{
    public static void Main()
    {
        DocumentEditor editor = new DocumentEditor();

        editor.CreateDocument("Hello, World!", new List<string> { "image1.jpg" }, new List<string> { "table1" });
        editor.CreateDocument("Hello, C#!", new List<string> { "image2.jpg" }, new List<string> { "table2" });

        editor.Undo(); // 输出: Undo: Text: Hello, World!, Images: image1.jpg, Tables: table1
        editor.Undo(); // 输出: No more actions to undo.
    }
}

代码解析

  1. Document 类:实现了 ICloneable 接口,并提供了 Clone 方法来深拷贝对象。
  2. DocumentEditor 类:维护了一个文档历史栈,每次创建文档时将其压入栈中。Undo 方法通过弹出栈顶元素来实现撤销功能。
  3. 客户端代码:创建文档并进行撤销操作,验证原型模式的效果。

案例 2:游戏中的角色克隆

场景描述
在一款游戏中,玩家可以选择不同的角色,并且可以快速切换角色。为了提高性能,可以使用原型模式来克隆角色对象,而不是每次都重新创建。
代码实现

using System;

// 角色类,实现 ICloneable 接口
public class Character : ICloneable
{
    public string Name { get; set; }
    public int Level { get; set; }
    public string Weapon { get; set; }

    // 克隆方法
    public object Clone()
    {
        return new Character
        {
            Name = this.Name,
            Level = this.Level,
            Weapon = this.Weapon
        };
    }

    public override string ToString()
    {
        return $"Name: {Name}, Level: {Level}, Weapon: {Weapon}";
    }
}

// 游戏管理器类
public class Game
{
    private Dictionary<string, Character> _characters = new Dictionary<string, Character>();

    public void AddCharacter(string name, Character character)
    {
        _characters[name] = character;
    }

    public Character GetCharacter(string name)
    {
        if (_characters.ContainsKey(name))
        {
            return (Character)_characters[name].Clone();
        }
        return null;
    }
}

// 使用示例
public class Program
{
    public static void Main()
    {
        Game game = new Game();

        Character warrior = new Character
        {
            Name = "Warrior",
            Level = 10,
            Weapon = "Sword"
        };

        game.AddCharacter("Warrior", warrior);

        Character clonedWarrior = game.GetCharacter("Warrior");
        Console.WriteLine(clonedWarrior); // 输出: Name: Warrior, Level: 10, Weapon: Sword

        // 修改克隆的角色
        clonedWarrior.Level = 20;
        Console.WriteLine(clonedWarrior); // 输出: Name: Warrior, Level: 20, Weapon: Sword

        // 原始角色不受影响
        Console.WriteLine(game.GetCharacter("Warrior")); // 输出: Name: Warrior, Level: 10, Weapon: Sword
    }
}

代码解析

  1. Character 类:实现了 ICloneable 接口,并提供了 Clone 方法来深拷贝对象。
  2. Game 类:维护了一个角色字典,提供添加角色和获取角色的方法。获取角色时返回的是克隆的对象。
  3. 客户端代码:创建角色并获取克隆的角色,验证原型模式的效果。

案例 3:图形编辑器中的图形克隆

场景描述
在一个图形编辑器中,用户可以创建各种图形(如圆形、矩形等),并可以复制图形。使用原型模式可以高效地克隆图形对象,避免重复创建复杂的图形对象。
代码实现

using System;
using System.Drawing;

// 图形基类,实现 ICloneable 接口
public abstract class Shape : ICloneable
{
    public Point Position { get; set; }
    public Color Color { get; set; }

    public abstract object Clone();
}

// 圆形类
public class Circle : Shape
{
    public int Radius { get; set; }

    public override object Clone()
    {
        Circle clonedCircle = new Circle
        {
            Position = this.Position,
            Color = this.Color,
            Radius = this.Radius
        };
        return clonedCircle;
    }

    public override string ToString()
    {
        return $"Circle at {Position} with radius {Radius} and color {Color}";
    }
}

// 矩形类
public class Rectangle : Shape
{
    public int Width { get; set; }
    public int Height { get; set; }

    public override object Clone()
    {
        Rectangle clonedRectangle = new Rectangle
        {
            Position = this.Position,
            Color = this.Color,
            Width = this.Width,
            Height = this.Height
        };
        return clonedRectangle;
    }

    public override string ToString()
    {
        return $"Rectangle at {Position} with width {Width} and height {Height} and color {Color}";
    }
}

// 图形编辑器类
public class GraphicsEditor
{
    private List<Shape> _shapes = new List<Shape>();

    public void AddShape(Shape shape)
    {
        _shapes.Add(shape);
    }

    public Shape CloneShape(Shape shape)
    {
        return (Shape)shape.Clone();
    }

    public void DisplayShapes()
    {
        foreach (var shape in _shapes)
        {
            Console.WriteLine(shape);
        }
    }
}

// 使用示例
public class Program
{
    public static void Main()
    {
        GraphicsEditor editor = new GraphicsEditor();

        Circle circle = new Circle
        {
            Position = new Point(50, 50),
            Color = Color.Red,
            Radius = 20
        };

        Rectangle rectangle = new Rectangle
        {
            Position = new Point(100, 100),
            Color = Color.Blue,
            Width = 50,
            Height = 30
        };

        editor.AddShape(circle);
        editor.AddShape(rectangle);

        Shape clonedCircle = editor.CloneShape(circle);
        clonedCircle.Position = new Point(70, 70);

        editor.AddShape(clonedCircle);

        editor.DisplayShapes();
        // 输出:
        // Circle at {X=50,Y=50} with radius 20 and color Color [Red]
        // Rectangle at {X=100,Y=100} with width 50 and height 30 and color Color [Blue]
        // Circle at {X=70,Y=70} with radius 20 and color Color [Red]
    }
}

代码解析

  1. Shape 类:抽象基类,实现了 ICloneable 接口,并提供了抽象的 Clone 方法。
  2. Circle 类 和 Rectangle 类:具体图形类,实现了 Clone 方法来深拷贝对象。
  3. GraphicsEditor 类:维护了一个图形列表,提供添加图形和克隆图形的方法。
  4. 客户端代码:创建图形并克隆图形,验证原型模式的效果。

优点

  1. 性能提升:通过克隆现有对象,避免了复杂的初始化过程,提高了性能。
  2. 代码复用:可以重用现有的对象,减少重复代码。
  3. 灵活:可以在运行时动态地创建对象,增加了系统的灵活性。

缺点

  1. 深拷贝和浅拷贝问题:需要特别注意对象的深拷贝和浅拷贝问题,否则可能会导致意外的行为。
  2. 类设计复杂:每个需要克隆的对象都需要实现 Clone 方法,增加了类的设计复杂度。
  3. 安全性问题:如果对象包含敏感数据,克隆时需要注意数据的安全性。
  4. 内存消耗:每次创建新文档时都会生成一个新的 Document 对象并将其推入栈中,可能会导致较大的内存消耗,尤其是在频繁创建和撤销文档的情况下。
  5. 性能问题:如果文档非常大,克隆操作可能会比较耗时,影响性能。

总结

这个示例展示了如何使用状态模式和克隆模式来实现文档编辑器的撤销功能。通过将文档的历史版本存储在栈中,并在需要时进行撤销操作,可以有效地管理文档的状态变化。同时,通过实现 ICloneable 接口,确保每次创建新文档时都能获得一个独立的副本,保证了文档状态的独立性。

12-12 03:17