原型模式(Prototype Pattren)
原型模式用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
考试结束了,学校放假了,我们又进入了全职提高班的学习模式:
6:50——起床(当然,年轻人总是爱睡个懒觉,早上总是赖床,有时也会拖到7:00起床)7:20——去中门买早餐带到机房
12:00——回家吃午饭,然后睡午觉
14:30——又开始了下午的学习
18:00——回家吃晚饭,然后去机房
22:00——回家,洗漱然后睡觉
我们就这样日复一日的生活着,只有周四,才能放松一下自己,好好休息休息。我们要有理想和目标,那样才能有生活学习的动力!
言归正传。下面我们来看看我们一天的生活和我们今天讲的原型模式有什么关联。从上面可以看出,我们每天的生活是一个循环,什么是循环呢?就是重复同样的动作,用原型模式的概念就是:用原型实例指定创建对象的种类,即我们一天的生活,如果要生成很多天的生活模式,只要通过赋值这些原型创建信的对象就可以了,而不必每次都要new一个实例对象。这样将大大节省创建对象所消耗的各种资源。下面我们就来看看具体怎么实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 原型模式
{
//原型类
class DayLife:ICloneable
{
private string getUp;
private string getFood;
private string noon;
private string afternoon;
private string dinner;
private string goHome;
public void setGetUp(string getup)
{
this.getUp = getup;
}
public void setGetFood(string getfood)
{
this.getFood = getfood;
}
public void setNoon(string noon)
{
this.noon = noon;
}
public void setAfternoon(string afternoon)
{
this.afternoon = afternoon;
}
public void setDinner(string dinner)
{
this.dinner = dinner;
}
public void setGoHome(string gohome)
{
this.goHome = gohome;
}
//显示
public void Display()
{
Console.WriteLine(getUp);
Console.WriteLine(getFood);
Console.WriteLine(noon);
Console.WriteLine(afternoon);
Console.WriteLine(dinner);
Console.WriteLine(goHome);
Console.WriteLine();
}
public object Clone()
{
return (object)this.MemberwiseClone();
}
}
class Program
{
static void Main(string[] args)
{
DayLife a = new DayLife();
a.setGetUp("6:50起床");
a.setGetFood("7:20去中门买早餐带到机房");
a.setNoon("12:00回家吃午饭,然后睡午觉");
a.setAfternoon("14:30又开始了下午的学习");
a.setDinner("18:00回家吃晚饭,然后去机房");
a.setGoHome("22:00回家,洗漱然后睡觉");
a.Display();
DayLife b = (DayLife)a.Clone();
b.setGetUp("早上赖床了,7:00才起床");
b.Display();
}
}
}
可以看到,只有第一个DayLife对象是通过new创建的,第二个是通过clone克隆生成的!这就是原型模式:通过一个已经存在的对象,利用其现有的条件,克隆生成一个全新的对象,在此基础上修改部分内容,形成全新对象实体!
在原型模式结构图中包含如下几个角色:
Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。
使用原型模式时,我们需要注意:
1.克隆对象时对象的构造方法不执行
一般在使用new操作符创建一个对象的时候,一定会执行类的构造方法,在构造方法中可以做一些数据加载或者初始化的操作。然而,在实现Cloneable接口的类中,重载clone方法产生新对象时,是不会执行类的构造方法的!
2.浅复制和深复制
浅复制:只复制本对象的原始数据类型,如int、float、String等,对于数组和对象引用等是不会复制的。
深复制:不但对原始数据类型做复制,对于对象中的数组和对象引用也做复制的行为,从而达到将对象完全复制的效果。主要优点:
1.当创建信的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
2.原型模式提供了简化的创建结构,工厂方法需要有一个与产品类登记结构相同的工厂等级结构,而原型中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品
3.可以使用深复制的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如回复到某一历史状态),可辅助实现撤销操作
主要缺点:
1.需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”
2.在实现深复制时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深复制,每一层对象对应的类都必须支持深复制,实现起来可能会比较麻烦使用场合:
1.产生对象过程比较复杂,舒适化需要许多资源时;
2.希望框架原型和产生对象分开时;
3.同一个对象可能会供其他调用者同事调用访问时;