创建型模式除了三大工厂和建造者模式,就剩下这两个了,原型模式和单例模式。下面就谈谈这两个模式。
一、原型模式
定义
原型模式(prototype):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
自己的理解:说白了就是复制。就像简历的例子一样,我们肯定是需要好多份简历不能每次都执行一遍程序,所以肯定得把制作简历抽象出一个类,用的时候实例化就OK了嘛,但是,如果需要1000份就非要实例化1000次吗?太浪费资源空间了吧,所以就用了一个非常洋气的方法叫clone。clone其实就是复制嘛,但是不能老克隆一模一样的吧,我想给别人也写个简历呢,所以就涉及到深复制和浅复制了。
UML类图
代码示例(简历)
namespace 原型模式
{
class Program
{
static void Main(string[] args)
{
Resume a = new Resume("大鸟");
a.SetPersonalInfo("男","29");
a.SetWorkExperience("1998-2000","xx公司");
Resume b = (Resume)a.Clone();
b.setPersonalInfo("女","55");
b.SetWorkExperinece("2000-3000","yy企业");
a.Display();
b.Display();
Console.Read();
}
}
#region 简历 深复制和浅复制
//工作经历
//class WorkExperience
class WorkExperience:ICloneable //工作经历实现ICloneable 接口 深复制 实现接口
{
private string workDate;
public string WorkDate
{
get { return workDate; }
set { workDate = value; }
}
private string company;
public string Company
{
get { return company; }
set { company = value; }
}
//深复制
public Object Clone()
{
return (Object)this.MemberwiseClone();
}
}
//简历
class Resume : ICloneable
{
private string name;
private string sex;
private string age;
private WorkExperience work; //引用工作经历对象
public Resume(string name)
{
this.name = name;
work = new WorkExperience();// 在“简历类”实例化的同时实例化“工作经历”
}
//提供clone方法调用的私有构造函数,以便克隆工作经历的数据
private Resume(WorkExperience work)
{
this.work = (WorkExperience)work.<span style="color:#3366ff;">Clone()</span>;
}
//设置个人信息
public void SetPersonalInfo(string sex, string age)
{
this.sex = sex;
this.age = age;
}
//设置工作经历
public void SetWorkExperinece(string workDate, string company)
{
work.WorkDate = workDate;//调用此方法是,给对象的两属性赋值
work.Company = company;
}
//显示
public void Display()
{
Console.WriteLine("{0} {1} {2}",name,sex,age );
Console.WriteLine("工作经历:{0} {1}",work.WorkDate,work.Company);
}
public Object Clone()
{
//return (Object)this.MemberwiseClone(); 浅复制
//调用私有的构造方法,让工作经历克隆完成,然后再给这个简历对象的相关字段赋值,最终返回一个深复制的简历对象
Resume obj = new Resume(this.work);
obj.name = this.name;
obj.sex = this.sex;
obj.age = this.age;
return obj;
}
internal void setPersonalInfo(string p1, string p2)
{
throw new NotImplementedException();
}
internal void SetWorkExperience(string p1, string p2)
{
throw new NotImplementedException();
}
}
#endregion
}
适用场景
初始化信息不发生变化的情况下,需要重复的创建对象。优点是隐藏对象创建细节,大大提高性能。
深复制和浅复制
浅复制:如果字段是值类型的,则对该字段进行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象,因此,原始对象及其副本引用同一对象。
深复制:是将指向内容复制到给当前对象新分配的缓冲区中的一种复制方式,把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象都复制一遍。
自己的理解:
A引用B,现在要复制A(二者区别针对引用类型)
浅复制: A1引用B
深复制: A1引用B1
浅复制不复制引用的对象,之前引用的谁,现在还引用谁
深复制会开辟空间把引用对象也复制过来
二、单例模式
单例模式是设计模式中最简单的模式之一。根本就没有涉及到其他复杂的类,就是在原来的类上加上双重锁或设置静态初始化使得这个类自己控制自己只能有一个实例。
定义
单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。
必要性
对于某些类来说,只有一个实例是很重要的。遇到多线程问题的时候,只能让一个线程工作,其他的等待。比如我们在打印的时候,可能一下选择了好几个打印任务,但是只能有一个正在工作的任务。在创建窗体的时候,如果不对创建窗体的唯一性进行控制,就可以创建多个相同的窗体,重复的对象是对内存资源的一种浪费。
代码示例(创建窗体)
amespace 单例模式
{
#region<span style="color:#ff0000;"> 懒汉模式 双重锁定</span>
class Singleton
{
private static Singleton instance;//抽出来成为全局变量
private static readonly object syncRoot = new object();//程序运行时创建一个静态只读的进程辅助对象
private Singleton() //构造方法用private,阻止外界new此类创建实例
{ }
public static Singleton GetINstance()//此方法是获得本类实例的唯一全局访问点
{
if (instance == null)// 没有创建实例,有两个进程等着,只能进去一个
{
lock(syncRoot )
{
if(instance == null)//保证如果有一个实例创建了,另一个就无法创建
{
instance = new Singleton();
}
}
}
return instance;
}
}
#endregion
#region <span style="color:#ff0000;">饿汉式单例类</span>
//封装的,阻止发生派生
public sealed class Singleton
{
<span style="color:#ff0000;">//静态初始化是在类被加载的时候就将它实例化
private static readonly Singleton instance = new Singleton();</span>
private Singleton() { }
public static Singleton GetInstance()
{
return instance;
}
}
#endregion
}
如何实现单例
第一步:全局访问
把类变量声明成全局变量,不要在某一事件中声明,这样可以判断类是否被实例化过。
第二步:实例化控制
方法一:双重锁定
这针对的是多线程问题。
if (instance == null)// 没有创建实例,有两个进程等着,只能进去一个
{
lock(syncRoot )
{
if(instance == null)//保证如果有一个实例创建了,另一个就无法创建
第一个if是判断是否已经创建过实例,如果没有,上把锁,只能进去一个线程。第二个if是判断第一线程有没有创建实例,如果创建了第二个线程就不能再创建。
由于该类是在第一次被引用的时候,才会将自己实例化,所以被称为懒汉式单例类。
方法二:静态实例化
这种方法不需要开发人员显示的编写线程安全代码,即可解决多线程环境下不安全的问题。这种静态初始化的方式是在自己被加载时就将自己实例化,所以被称为饿汉式单例类。
总结
在实际开发过程中肯定会有好多涉及到多线程的问题,所以单例模式也要掌握熟练。单例模式主要就是解决多线程的安全问题。如果开发过程中遇到只需要一个实例的问题就考虑考虑单例模式,如果对一个类重复调用,内容没有多大改变,跟复制似的就考虑原型模式。