原型模式可以动态的创建一个对象而不需要提供专门的new()操作,这无疑是一种非常有效的方式,快速的创建一个新的对象。
一、使用场景:
1、如果说我们的对象类型不是刚开始就能确定,而是这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的类型更容易。
2、获取某一个特定状态下的对象,这个前提很重要,这点怎么理解呢,例如有的时候我们需要对比一个对象经过处理后的状态和处理前的状态是否发生过改变,可能我们就需要在执行某段处理之前,克隆这个对象此时状态的副本,然后等执行后的状态进行相应的对比,这样的应用在项目中也是经常会出现的。
假设我们有这样的需求,我们在ORM框架的设计中,经常会遇到这样的问题,我们在处理某个对象的编辑状态的时候,我们想框架给我们生成的更新数据库的SQL语句,不包含数据列没有发生变化的列,不要出现在更新语句中,这个时候,可能一个方案会是,编辑前克隆一个对象,然后等编辑后提交的时候,生成相应的语句时进行对比之前克隆的对象,看看是否数据发生变化,如果说对象的部分数据列发生变化,那么就只是把变化的数据列进行更新。
3、当我们在处理一些对象比较简单,并且对象之间的区别很小,可能只是很固定的几个属性不同的时候,可能我们使用原型模式更合适,例如我们生活中的彩虹的七彩的颜色,我们只需要根据现有的一个颜色对象,克隆一个新的颜色对象,然后修改具体的颜色的值就可以满足要求。
二、代码走起来
我们首先创建一个类Person类,该对象实现ICloneable(用来复制对象的接口)接口
public class Person : ICloneable
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
public Person()
{
Console.WriteLine("当前执行时间:" + DateTime.Now);
}
/// <summary>
/// 地址
/// </summary>
public Address address { get; set; }
/// <summary>
/// 复制对象的方法
/// </summary>
/// <returns></returns>
public object Clone()
{
return this.MemberwiseClone();
}
static void Main(string[] arge)
{
var person = new Person()
{
Name = "jack",
Age = 20,
address = new Address()
{
Privince = "中国江苏",
City = "苏州"
}
};
//动态的获取一个对象的运行状态
var personCopy = (Person)person.Clone();
Console.WriteLine("运行结束");
Console.ReadLine();
}
}
我们在添加一个Address类,用来表示person的地址,具体的类为
public class Address
{
public string Privince { get; set; }
public string City { get; set; }
}
我们看运行结果:
我们可以看到在获取personCopy对象的时候没有用到new的方法而是用到复制方法;
通过调用方法内部的MemberwiseClone方法来复制一个对象,这种复制方式叫做浅复制,浅复制出来的对象中,对于值类型的成员浅复制是在副本中重新创建的成员,对于引用类型的成员对象和对象副本共用同一个引用对象,那么不管是在对象还是对象副本中修改了相应的引用成员了之后,那么这个引用类型的成员就会发生变化。因为2个对象指向同一个内存地址,那么任何一个修改操作都会产生改变,我们修改下代码;看看运行结果,修改的代码如下:
public class Person : ICloneable
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
public Person()
{
Console.WriteLine("当前执行时间:" + DateTime.Now);
}
/// <summary>
/// 地址
/// </summary>
public Address address { get; set; }
/// <summary>
/// 复制对象的方法
/// </summary>
/// <returns></returns>
public object Clone()
{
return this.MemberwiseClone();
}
static void Main(string[] arge)
{
var person = new Person()
{
Name = "jack",
Age = 20,
address = new Address()
{
Privince = "中国江苏",
City = "苏州"
}
};
//动态的获取一个对象的运行状态
var personCopy = (Person)person.Clone();
person.Age = 1000;
person.address.City = "苏州吴江区";
Console.WriteLine($"person对象的Age的值是:{person.Age}");
Console.WriteLine($"personCopy对象的Age的值是:{personCopy.Age}");
Console.WriteLine($"person对象的city值是:{person.address.City}");
Console.WriteLine($"personCopy对象的city值是:{personCopy.address.City}");
Console.WriteLine("运行结束");
Console.ReadLine();
}
}
person的age是1000但是personCopy还是20,但是无论person还是personCopy的city都是苏州吴江区都做了改变
深复制:
深复制就是复制后的对象无论对于值类型的成员还是引用类型的成员都是重新分配空间,对于要深复制对象的时候的方案,还可以通过序列化的形式来进行对象的复制。下面我们来通过序列化的形式来实现原型模式:
通过序列化来实现原型模式要注意的点就是要序列化的对象需要添加序列化标示[Serializable]
实现的代码如下:
[Serializable]
public class Address
{
public string Privince { get; set; }
public string City { get; set; }
}
[Serializable]
public class Person
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
public Person()
{
Console.WriteLine("当前执行时间:" + DateTime.Now);
}
/// <summary>
/// 地址
/// </summary>
public Address address { get; set; }
/// <summary>
/// 复制对象的方法
/// </summary>
/// <returns></returns>
public object Clone(Person person)
{
//return this.MemberwiseClone();
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, person);
memoryStream.Seek(0, SeekOrigin.Begin);
var newPerson = (Person)binaryFormatter.Deserialize(memoryStream);
memoryStream.Close();
return newPerson;
}
static void Main(string[] arge)
{
var person = new Person()
{
Name = "jack",
Age = 20,
address = new Address()
{
Privince = "中国江苏",
City = "苏州"
}
};
//动态的获取一个对象的运行状态
var personCopy = (Person)person.Clone(person);
person.address.City = "苏州吴江区";
Console.WriteLine($"person对象的city值是:{person.address.City}");
Console.WriteLine($"personCopy对象的city值是:{personCopy.address.City}");
Console.WriteLine("运行结束");
Console.ReadLine();
}
}
运行结果:
原型模式的适用环境:
1:创建新对象成本较大(例如初始化时间长,占用CPU多或占太多网络资源),新对象可以通过复制已有对象来获得,如果相似对象,则可以对其成员变量稍作修改。
2:系统要保存对象的状态,而对象的状态很小。
3:需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的组合状态,通过复制原型对象得到新实例可以比使用构造函数创建一个新实例更加方便。
缺点:
(1):需要为每一个类配置一个克隆方法,而且该克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
(2):在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重签到引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。