Prototype 原型模式简介与 C# 示例【创建型4】【设计模式来了_4】

发布时间 2023-08-18 10:52:22作者: 橙子家

〇、简介

1、什么是原型模式?

一句话解释:

  针对比较耗时的对象创建过程,通过原型的 Clone 方法来克隆对象,而非重新创建。

原型设计模式(Prototype Design Pattern)是一种创建型设计模式,其主要目的是通过复制已有对象来创建新的对象,而无需通过实例化类并初始化其属性。这种模式在需要创建相似对象时非常有用,尤其是当创建过程较为复杂或耗时时。

原型设计模式中的 Clone() 方法可以是深拷贝或浅拷贝,具体取决于实现的方式。

官方意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 

 一个比喻:(多个班级与礼堂)

  多个班级需要轮流在礼堂举行活动,门口有海报标注时间,相当于礼堂对象的引用。学校不会为每个班级修一座礼堂,这样太耗时,而是每个班级共用一个礼堂,使用时按照海报的时间即可。

2、优缺点和使用场景

优缺点:

  • 简化对象创建。原型设计模式通过克隆现有对象来创建新对象,避免了重新构造复杂的对象。这样可以简化对象的创建过程,减少了重复代码的编写
  • 提高性能。由于原型设计模式避免了重新执行初始化过程,通过复制现有对象来创建新对象,可以提高系统的性能,特别是在对象创建成本较高的情况下。
  • 隐藏对象创建细节。原型设计模式将对象的创建细节封装在原型对象中,客户端只需通过克隆操作即可创建新对象,无需了解对象创建的具体实现。这样可以降低系统的耦合度,提高代码的灵活性和可维护性
  • 动态添加或删除对象。原型设计模式通过克隆现有对象来创建新对象,可以方便地增加或减少对象的数量,实现了系统的灵活性和可扩展性。

原型设计模式在合适的场景下可以提供很多优点,但在使用时需要注意克隆方法的正确性和对象的深拷贝问题。

  • 需要对每个类进行克隆操作。在使用原型设计模式时,需要为每个类都实现克隆方法,这可能会增加代码量和维护成本。
  • 克隆方法的正确性。实现克隆方法时需要保证被克隆对象的所有成员变量都正确地进行复制,包括基本数据类型和引用类型。如果某个成员变量未正确克隆,可能会导致意外的行为或错误
  • 对象的深拷贝问题。在实现原型设计模式时,需要注意对象的深拷贝问题。如果对象中包含了其他对象的引用,需要确保对这些引用对象也进行递归的克隆操作,以实现真正的深拷贝

适用场景:

  • 创建一个对象的过程非常复杂或者耗时时,可以使用原型设计模式。通过复制现有对象来创建新对象,可以避免重新执行复杂的初始化过程,从而提高性能。
  • 当需要避免与对象创建相关的细节,并希望客户端能够独立于具体类进行实例化时,可以使用原型设计模式。通过原型模式,客户端可以通过复制原型对象来创建新对象,而无需了解对象创建的具体实现。
  • 当系统需要动态地添加或删除一些对象时,可以使用原型设计模式。通过克隆现有对象来创建新对象,可以方便地增加或减少对象的数量。
  • 当希望改变和定制一个对象的结构,同时又希望保持其行为不变时,可以使用原型设计模式。通过克隆对象并对其进行修改,可以快速生成新对象,同时保留原始对象的行为。

实际使用场景举例:

  • 在游戏开发中,角色的创建和管理是一个重要的问题。原型设计模式可以用于创建和管理大量的游戏角色。通过克隆原型角色对象,可以快速生成新的角色对象,减少了创建角色的开销。
  • 在数据库应用中,经常需要对数据库记录进行增删改查操作。使用原型设计模式,可以通过复制已有的数据库记录对象来创建新的记录对象,然后对新对象进行修改或插入操作,从而实现数据的快速操作和灵活性。
  • 在图形编辑器中,用户可以通过复制和粘贴操作来创建新的图形对象。这时可以使用原型设计模式,将已有的图形对象作为原型,在需要时通过克隆操作创建新的图形对象,避免了重新构造复杂的图形对象。
  • 在操作系统中,进程的创建和销毁是一个频繁且耗资源的操作。使用原型设计模式,可以通过克隆已有的进程对象来创建新的进程对象,提高了进程创建的效率
  • 原型设计模式可以与对象池模式结合使用,实现对象的缓存和复用。通过创建一组原型对象,并在需要时通过克隆操作获取对象,可以避免频繁地创建和销毁对象,提高系统的性能

一、原型模式的简单实现

下面例举个示例,父类实现 ICloneable 接口的 Clone() 方法:

// 父类:图形类
abstract class Shape : ICloneable // 实现接口 ICloneable 中的方法 Clone
{
    protected string type;
    public abstract void Draw();
    public object Clone()
    {
        return this.MemberwiseClone(); // 创建当前对象的浅拷贝,两个引用指向同一个对象
        // 注意:
        // 浅拷贝就相当于给引用对象又起了另外一个名字,小黄和修勾勾都指的是这一只小狗;
        // 深拷贝就是用克隆技术,把小黄复刻了一个长得一模一样的小黄二号,名字不同的同时指的对象也不同。
    }
}
// 圆形类,继承图形类
class Circle : Shape
{
    public Circle()
    {
        type = "Circle";
    }
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}
// 矩形类,也继承图形类
class Rectangle : Shape
{
    public Rectangle()
    {
        type = "Rectangle";
    }
    public override void Draw()
    {
        Console.WriteLine("Drawing a rectangle");
    }
}
class Program
{
    static void Main(string[] args)
    {
        Circle circle = new Circle(); // 创建一个圆形对象
        Circle clonedCircle = (Circle)circle.Clone(); // 克隆圆形对象(复制了对象的引用)
        // circle 和 clonedCircle 两个引用,指向同一个对象
        clonedCircle.Draw();
        
        Rectangle rectangle = new Rectangle(); // 创建一个矩形对象        
        Rectangle clonedRectangle = (Rectangle)rectangle.Clone(); // 克隆矩形对象
        // rectangle 和 clonedRectangle 两个引用,指向同一个对象
        clonedRectangle.Draw();
        Console.ReadKey();
    }
}

如下输出结果,调用克隆对象的 Draw() 得出:

  

二、结构

根据上一章节的示例代码,简单画一个 UML 图:

Shape:原型抽象,声明一个抽象类,实现 ICloneable 接口的 Clone() 方法,使得本抽象类可以被克隆。

Circle、Rectangle:原型,继承抽象类 Shape,实现自身可被克隆的性质。

Client:创建可被克隆的原型,并通过 Clone() 方法复制原型。

三、原型模式在 .Net 中的实际应用

例如在数据库连接管理的应用。

在数据库应用程序中,连接到数据库可能是一个耗时且资源密集的操作。为了避免每次都重新创建连接,可以使用原型设计模式来克隆现有的连接对象。

public class DbConnection : ICloneable
{
    public string ConnectionString { get; set; }
    // ...
    
    public object Clone()
    {
        return this.MemberwiseClone();
    }
}
// 使用示例:
DbConnection originalConnection = new DbConnection { ConnectionString = "..." };
DbConnection clonedConnection = (DbConnection)originalConnection.Clone();

四、相关模式

Prorotype 和 AbstractFactory 模式在某些方面是互相竞争的。但是它们也可以一起使用,AbstractFactory 可以存储一个被克隆对象的原型集合,并且返回产品对象。

  • 原型设计模式的主要目的是通过克隆现有对象来创建新对象,以避免重复创建相似的对象。它适用于需要动态生成对象的情况,并且能够在运行时选择要克隆的对象。
  • 抽象工厂模式的主要目的是提供一个接口来创建一系列相关或依赖对象的家族,而无需指定具体的类。它适用于需要创建一组相关对象的情况,并且能够在运行时切换整个对象家族。

大量使用 Composite(组合) 和 Decorator(装饰) 模式的设计,通常也可以用到 Prototype 模式。