【手写源码-设计模式5】-原型模式-基于学习Java,C#,C++场景

1:主题拆解

①基本介绍

②模式演变过程

③C#内存分配机制

④如何深拷贝

⑤原型模式优缺点

⑥适用场景

2:基本介绍

 原型模式:使用原型实例指定创建对象的种类,并且通过克隆这些原型创建新的对象。

原型模式是一种对象创建型模式。

原型模式的工作原理很简单,将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象克隆自己来实现创建过程。原型模式是一种另类的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法实现。

通过克隆方法创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆多产生的对象进行的修改不会对原型对象造成任何的影响,每一个克隆的对象都是相互独立的,通过不同的方式对克隆对象进行修改之后,可以得到一系列相似但不完全相同的对象。

3:模式演变过程

1:前置准备

模拟类的创建耗时,耗资源等

public class Student
{
    public Student()
    {
        Thread.Sleep(2000);
        long lResult = 0;
        for (int i = 0; i < 1000000; i++)
        {
            lResult += i;
        }
        Console.WriteLine("{0}被构造..", this.GetType().Name);
    }
    public int Id { get; set; }
    public string Name { get; set; }


    public void Study()
    {
        Console.WriteLine("{0}在学习设计模式", this.Name);
    }
}

2:对象复用

Student student = new Student()
{
    Id = 1121,
    Name = "Micro"
};
for (int i = 0; i < 5; i++)
{ 
    student.Study();
}

分析:为了避免每次创建对象时出现的耗时耗资源的情况。此处引入采用对象复用,循环的就是最简单的,不同的方法/类/线程/类库,重用对象。

3:单例模式

public class StudentSingleton
{
    /// <summary>
    /// 1 构造函数私有化--避免随意构造
    /// </summary>
    private StudentSingleton()
    {
        Thread.Sleep(2000);
        long lResult = 0;
        for (int i = 0; i < 1000000; i++)
        {
            lResult += i;
        }
        Console.WriteLine("{0}被构造..", this.GetType().Name);
    }
    /// <summary>
    /// 3 私有的静态字段--内存唯一,不会释放,且在第一次使用这个类被初始化且只初始化一次
    /// </summary>
    private static StudentSingleton _Student = new StudentSingleton()
    {
        Id = 123,
        Name = "xiaokeai"
    };
    /// <summary>
    /// 2 公开的静态方法来提供实例
    /// </summary>
    /// <returns></returns>
    public static StudentSingleton CreateInstance()
    {
        return _Student;
    }


    public int Id { get; set; }
    public string Name { get; set; }


    public void Study()
    {
        Console.WriteLine("{0}在学习设计模式", this.Name);
    }
}
for (int i = 0; i < 5; i++)
{
    StudentSingleton student = StudentSingleton.CreateInstance();
    student.Study();
}
分析:单例模式:保证整个进程中,该对象只有一个实例

4:多个实例

StudentSingleton student1 = StudentSingleton.CreateInstance();
StudentSingleton student2 = StudentSingleton.CreateInstance();
student1.Id = 234;
student1.Name = "Micro";
student2.Id = 345;
student2.Name = "Tudou";
student1.Study();
student2.Study();

分析:整个进程只有一个实例,是否可以出现2个学生呢,从执行结果中我们可以看出,后面会覆盖前面的,即单例可以对象复用,但是会出现覆盖。

能不能不覆盖,但是性能也高?

5:原型模式

①引入原型模式类

public class StudentPrototype
{
    /// <summary>
    /// 1 构造函数私有化--避免随意构造
    /// </summary>
    private StudentPrototype()
    {
        Thread.Sleep(2000);
        long lResult = 0;
        for (int i = 0; i < 1000000; i++)
        {
            lResult += i;
        }
        Console.WriteLine("{0}被构造..", this.GetType().Name);
    }
    /// <summary>
    /// 3 私有的静态字段--内存唯一,不会释放,且在第一次使用这个类被初始化且只初始化一次
    /// </summary>
    private static StudentPrototype _Student = new StudentPrototype()
    {
        Id = 123,
        Name = "xiaokeai"         
    };
    /// <summary>
    /// 2 公开的静态方法来提供实例
    /// </summary>
    /// <returns></returns>
    public static StudentPrototype CreateInstance()
    {
        StudentPrototype student = (StudentPrototype)_Student.MemberwiseClone(); 
        return student;
    }  
    public int Id { get; set; }
    public string Name { get; set; }    
    public void Study()
    {
        Console.WriteLine("{0}在学习设计模式", this.Name);
    }
} 

②上端调用

StudentPrototype student1 = StudentPrototype.CreateInstance();
StudentPrototype student2 = StudentPrototype.CreateInstance(); 
student1.Id = 234;
student1.Name = "micro";  
student2.Id = 345;
student2.Name = "tudou";
student1.Study();
student2.Study()

分析:由于原型模式中的MemberwiseClone为内存拷贝,不走构造函数,直接内存copy,所以没有性能损失;而且产生的是新对象,因此不会出现覆盖。

其实此时的拷贝只是浅拷贝,即只拷贝引用,何为浅拷贝,我们继续拆解

6:原型模式升级

①原型模式类添加对象属性Class

public class StudentPrototype
{
    /// <summary>
    /// 1 构造函数私有化--避免随意构造
    /// </summary>
    private StudentPrototype()
    {
        Thread.Sleep(2000);
        long lResult = 0;
        for (int i = 0; i < 1000000; i++)
        {
            lResult += i;
        }
        Console.WriteLine("{0}被构造..", this.GetType().Name);
    }
    /// <summary>
    /// 3 私有的静态字段--内存唯一,不会释放,且在第一次使用这个类被初始化且只初始化一次
    /// </summary>
    private static StudentPrototype _Student = new StudentPrototype()
    {
        Id = 123,
        Name = "xiaokeai",
        Class = new Class()
        {
            ClassId = 1,
            ClassName = "设计模式"
        }
    };
    /// <summary>
    /// 2 公开的静态方法来提供实例
    /// </summary>
    /// <returns></returns>
    public static StudentPrototype CreateInstance()
    {
        StudentPrototype student = (StudentPrototype)_Student.MemberwiseClone();
        //MemberwiseClone:内存拷贝,不走构造函数,直接内存copy,所以没有性能损失;而且产生的是新对象----浅拷贝--只拷贝引用 
        return student;
    } 


    public static StudentPrototype CreateInstanceSerialize()
    {
        return SerializeHelper.DeepClone<StudentPrototype>(_Student);
    }


    public int Id { get; set; }
    public string Name { get; set; }
    public Class Class { get; set; }


    public void Study()
    {
        Console.WriteLine("{0}在学习设计模式", this.Name);
    }
} 
public class Class
{
    public int ClassId { get; set; }
    public string ClassName { get; set; }
}

 ②上端调用 

StudentPrototype student1 = StudentPrototype.CreateInstance();
StudentPrototype student2 = StudentPrototype.CreateInstance();
StudentPrototype student3 = StudentPrototype.CreateInstance();
student1.Id = 234;
student1.Name = "micro";
student1.Class.ClassId = 2;
student1.Class.ClassName = "java";                  

student2.Id = 345;
student2.Name = "tudou";
student2.Class.ClassId = 3;
student2.Class.ClassName = "c#";

student3.Id = 456;
student3.Name = "yinbao";
student3.Class = new Class()
{
    ClassId = 4,
    ClassName = "c++"
};
student1.Study();
student2.Study();
student3.Study();

问题:对于student1,student2,student3的ClassId 是什么?全部覆盖 444或者全部不覆盖234。

结论:ClassId依次为334,即student2会把student1的ClassId覆盖,而student3的ClassId为全新的4。其中student1,student2,student3三个对象的Name不覆盖,这个在上面就已经证明。

分析:此处的Class为浅拷贝,因此student2会把student1的ClassId覆盖,而student3换了个引用,因此是新的对象。

4:C#内存分配机制

进程堆(进程唯一),线程栈(每个线程一个)

引用类型在堆里,值类型在栈里,变量都在栈里。

1:StudentPrototype内存分配图解

 问题:为什么不放在一个内存空间,为啥搞多个

因为长度不确定

int--32--固定长度

string--不知道长度

Class--更不知道长度

没办法放到空间

2:MemberwiseClone浅拷贝分配图解

 分析:从上图我们就可以知道对应上面原型模式升级中由于浅拷贝只是拷贝了引用,实际上Class是指定的同一个实例对象,因此在更改ClassId与ClassName时出现了覆盖。

3:New新对象

分析:从上图我们就可以知道对应上面原型模式升级中New了一个新的Class对象,因此此时是指向新的实例。

4:string类型的为何没有覆盖

从上面的图形可以知道,MemberwiseClone中只是拷贝了引用,那对name直接赋值更为何没有出现实例被覆盖的情况?

因为string类型的 ="xxx" 等同于 new String("xxx"); 开辟新的空间,不影响之前的。实际上string是不可修改的。

5:如何深拷贝

1:直接new

例如将上面的原型模式拷贝部分进行直接new对象

public static StudentPrototype CreateInstance()
{
    StudentPrototype student = (StudentPrototype)_Student.MemberwiseClone();
    //MemberwiseClone:内存拷贝,不走构造函数,直接内存copy,所以没有性能损失;而且产生的是新对象----浅拷贝--只拷贝引用
    student.Class = new Class()
    {
        ClassId = student.Class.ClassId,
        ClassName = student.Class.ClassName
    };
    //把引用的地址重新赋值,完成了深copy--不仅拷贝引用,还得拷贝引用类型的值
    return student;
}

2:子类型提供原型方式

每一层都提供原型模式的方式实例化

3:序列化反序列化

①提供序列化帮助类

public class SerializeHelper
{
    public static string Serializable(object target)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            new BinaryFormatter().Serialize(stream, target);


            return Convert.ToBase64String(stream.ToArray());
        }
    }
    public static T Derializable<T>(string target)
    {
        byte[] targetArray = Convert.FromBase64String(target);


        using (MemoryStream stream = new MemoryStream(targetArray))
        {
            return (T)(new BinaryFormatter().Deserialize(stream));
        }
    }
    public static T DeepClone<T>(T t)
    {
        return Derializable<T>(Serializable(t));
    }
}

②原型模式中添加

public static StudentPrototype CreateInstanceSerialize()
{
    return SerializeHelper.DeepClone<StudentPrototype>(_Student);
}

③序列化类上加上特性标签

[Serializable]
public class Class
{
    public int ClassId { get; set; }
    public string ClassName { get; set; }
}
[Serializable]
public class StudentPrototype
{ 
    
}

6:优缺点

1:优点

        简化创建过程:当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率

        扩展性较好:由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少具体原型类对系统都没有任何影响

        简化创建结构:原型模式提供了简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无需专门的工厂类来创建产品

        保存状态:可以使用深克隆的方式保存对象的状态。使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用,例如恢复到某一历史状态,可辅助实现撤销操作

2:缺点

         修改不方便:需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造的时候,需要修改源代码,违背了OCP(开放闭合原则)

        深克隆需要嵌套类支持:在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象都必须支持深克隆,实现起来可能比较麻烦

7:适用场景

1:类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等。

2:通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

3:需要快速获取大量新对象。例如画棋盘上的很多格子,每个格子就是一个类的实例。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不要迷恋发哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值