PrototypePattern-原型模式

发布时间 2023-08-09 17:46:43作者: ZHIZRL

在C#中,原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过克隆现有对象来创建新对象,而无需依赖于显式的构造函数。原型模式通过复制现有对象的属性和状态,创建一个新对象,并在需要创建对象时返回这个克隆的副本。

原型模式的核心是使用原型接口或基类来定义克隆方法,并让具体类实现该方法来实现克隆功能。在C#中,原型模式的实现方式可以使用对象的MemberwiseClone()方法或序列化和反序列化来实现。

原型模式的意义在于对象的复用,减小对象构建对资源的开销

1.单例原型

单例模式:保证整个进程中,该对象只有一个实例

public class StudentSingleton
{
    public int Id { get; set; }
    public string Name { get; set; }
    /// <summary>
    /// 构造函数私有化
    /// </summary>
    private StudentSingleton()
    {
        Console.WriteLine("{0}被构造..", this.GetType().Name);
    }
    /// <summary>
    /// 静态字段
    /// </summary>
    private static StudentSingleton _Student = new StudentSingleton()
    {
        Id = 001,
        Name = "小明"
    };
    /// <summary>
    /// 开放的静态方法获取实例
    /// </summary>
    /// <returns></returns>
    public static StudentSingleton CreateInstance()
    {
        return _Student;
    }
    public void Study()
    {
        Console.WriteLine("{0}正在学习", this.Name);
    }
}
internal class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 3; i++)
        {
            StudentSingleton student = StudentSingleton.CreateInstance();
            student.Study();
        }
        //输出结果
        //StudentSingleton被构造..
        //小明正在学习
        //小明正在学习
        //小明正在学习
    }
}

扩展:C#内存分配机制

C#中的内存分配面向对象,对于值类型(如int、float)和引用类型(如string、类)的内存分配方式有所不同

int 的内存分配:int 是值类型,它的值直接存储在栈上。当声明一个 int 变量时,分配的内存空间大致等于一个 int 的大小(通常为4字节)。

string 的内存分配:string 是引用类型,它的实例存储在托管堆(Managed Heap)上。当使用 new 关键字创建一个 string 对象时,会在堆上分配一块内存空间来存储字符串的值。

{
    Console.WriteLine("***************Singleton**************");
    StudentSingleton student1 = StudentSingleton.CreateInstance();
    StudentSingleton student2 = StudentSingleton.CreateInstance();
    student1.Id = 002;
    student1.Name = "小红";

    student2.Id = 003;
    student2.Name = "小黄";

    Console.WriteLine(student1.Id);
    Console.WriteLine(student1.Name);
    Console.WriteLine(student2.Id);
    Console.WriteLine(student2.Name);
    //输出结果
    //3
    //小黄
    //3
    //小黄
}

单例模式整个进程只有一个实例,在C#内存分配机制中student1与student2属于引用类型,指向堆的同一块区域。所以后面的会覆盖前面的。

2.拷贝原型

MemberwiseClone()方法:内存拷贝,不走构造函数,直接内存copy,所以没有性能损失;

public class Classroom
{
    public int ClassroomId { get; set; }
    public string ClassroomName { get; set; }
}
public class StudentPrototype
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Classroom classroom { get; set; }

    /// <summary>
    /// 构造函数私有化
    /// </summary>
    private StudentPrototype()
    {
        Console.WriteLine("{0}被构造..", this.GetType().Name);
    }
    /// <summary>
    /// 静态字段
    /// </summary>
    private static StudentPrototype _Student = new StudentPrototype()
    {
        Id = 001,
        Name = "小明",
        classroom = new Classroom()
        {
            ClassroomId = 1,
            ClassroomName = "小学"
        }
    };
    /// <summary>
    /// 开放的静态方法获取实例
    /// </summary>
    /// <returns></returns>
    public static StudentPrototype CreateInstance()
    {
        StudentPrototype student = (StudentPrototype)_Student.MemberwiseClone();
        return student;
    }
    public void Study()
    {
        Console.WriteLine("{0}{1}正在{2}号{3}学习", this.Name, this.Id, this.classroom.ClassroomId, this.classroom.ClassroomName);
    }
}
{
    Console.WriteLine("***************Prototype**************");
    StudentPrototype student1 = StudentPrototype.CreateInstance();
    StudentPrototype student2 = StudentPrototype.CreateInstance();
    StudentPrototype student3 = StudentPrototype.CreateInstance();
    student1.Id = 002;
    student1.Name = "小红";
    student1.classroom.ClassroomId = 2;
    student1.classroom.ClassroomName = "初中";
    //因为string类型的 ="CodeMan" 等同于 new String("CodeMan"); 开辟新的空间,不影响之前的-----实际上string是不可修改的----

    student2.Id = 003;
    student2.Name = "小黄";
    student2.classroom.ClassroomId = 3;
    student2.classroom.ClassroomName = "高中";

    student3.Id = 004;
    student3.Name = "小蓝";
    student3.classroom = new Classroom()
    {
        ClassroomId = 4,
        ClassroomName = "大学"
    };
    student1.Study();
    student2.Study();
    student3.Study();
    //输出结果
    //StudentPrototype被构造..
    //小红2正在3号高中学习
    //小黄3正在3号高中学习
    //小蓝4正在4号大学学习
}

student1与student2的classroom属性属于引用类型,MemberwiseClone()方法属于浅拷贝,只拷贝classroom属性的引用地址。

StudentPrototype中的Name属性也属于引用类型(string),但student1.Name = "小红"等同于student1.Name = new String("小红"),这是String类型的特性,每次重新赋值只会重新开辟一块新的空间。

student3的classroom属性把引用的地址重新赋值,完成了深拷贝——不仅拷贝引用,还得拷贝引用类型的值。