@w1992wishes
2018-03-13T14:24:11.000000Z
字数 10443
阅读 886
设计模式
创建型模式
本文的结构如下:
我是一个很幼稚的人,所以经常会有很幼稚的想法,比如,有时候上着班我就在想,要是我能够影分身多好,这样,我可以让一号分身陪女朋友和家人,二号分身上班敲代码,三号分身街头卖烤串,四号分身被窝玩游戏......
我的可笑想法在当下是不现实的,但在软件开发中,却是非常务实的。设计模式中有一个模式,可以通过一个原型对象克隆出多个一模一样的对象,该模式称之为原型模式。
在使用原型模式时,我们需要首先创建一个原型对象,再通过复制这个原型对象来创建更多同类型的对象。一般这个原型对象的实例化很复杂,需要消耗很多的硬件资源或者数据资源,就像引言中说的“我”,是经过20多年的实例化,消耗了大量的粮食才构造成功的。
原型模式(Prototype Pattern):当创建给定类的实例化过程很复杂或者代价很昂贵时,可以使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程,拷贝通常是通过克隆方法实现。原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。
需要注意的是克隆有深克隆和浅克隆之分。
浅克隆: 在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
深克隆: 在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
clone()方法是Object中的一个方法,其源代码如下:
protected native Object clone() throws CloneNotSupportedException;
可以发现:
JNI是Java Native Interface的 缩写。从Java 1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java虚拟机实现下。
一般需要四个步骤:
Cloneable接口仅仅是一个标志接口,而且这个标志也仅仅是针对Object类中 clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出 CloneNotSupportedException 异常。
public class Student implements Cloneable{
private String name;
private int age;
private Professor professor;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Professor getProfessor() {
return professor;
}
public void setProfessor(Professor professor) {
this.professor = professor;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", professor="
+ professor + "]";
}
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class Professor {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Professor [name=" + name + ", age=" + age + "]";
}
}
public class ShadowCopy {
public static void main(String[] args) {
Professor p1 = new Professor();
p1.setName("Professor Zhang");
p1.setAge(30);
Student s1 = new Student();
s1.setName("xiao ming");
s1.setAge(18);
s1.setProfessor(p1);
System.out.println(s1);
try {
Student s2 = (Student) s1.clone();
Professor p2 = s2.getProfessor();
p2.setName("Professor Li");
p2.setAge(45);
s2.setProfessor(p2);
System.out.println("复制后的:s1 = " + s1);
System.out.println("复制后的:s2 = " + s2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
测试结果会发现复制后打印出来的s1和s2结果是一样的,s1和s2的导师都变成了45岁的Professor Li,显然这并不是期望的结果,产生这个结果的原因lone()方法实现的是浅克隆,对象引用professor只是复制了其引用,s1和s2仍是指向相同的地址块。
实现深克隆,可以在原clone()方法基础上改进一下,如下:
public class Student implements Cloneable{
private String name;
private int age;
private Professor professor;
public Student(String name, int age, Professor professor){
this.name = name;
this.age = age;
this.professor = professor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Professor getProfessor() {
return professor;
}
public void setProfessor(Professor professor) {
this.professor = professor;
}
public Object clone(){
Student o = null;
try {
o = (Student)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
o.professor = (Professor)professor.clone();
return o;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", professor="
+ professor + "]";
}
}
public class Professor implements Cloneable{
private String name;
private int age;
public Professor(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Object clone(){
Object o = name;
try {
o= super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return o;
}
@Override
public String toString() {
return "Professor [name=" + name + ", age=" + age + "]";
}
}
public class DeepClone {
public static void main(String[] args) {
Professor p=new Professor("wangwu",50);
Student s1=new Student("zhangsan",18, p);
System.out.println(s1);
Student s2=(Student)s1.clone();
s2.getProfessor().setName("maer");
s2.getProfessor().setAge(40);
System.out.println("复制后的:s1 = " + s1);
System.out.println("复制后的:s2 = " + s2);
}
}
也可以利用序列化反序列化来实现深克隆:
public class Student implements Serializable {
private String name;
private int age;
private Professor professor;
public Student(String name, int age, Professor professor){
this.name = name;
this.age = age;
this.professor = professor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Professor getProfessor() {
return professor;
}
public void setProfessor(Professor professor) {
this.professor = professor;
}
public Object deepClone() throws IOException, ClassNotFoundException {
//将对象写到流中
ByteArrayOutputStream bo=new ByteArrayOutputStream();
ObjectOutputStream oo=new ObjectOutputStream(bo);
oo.writeObject(this);
//从流中读出来
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi=new ObjectInputStream(bi);
return oi.readObject();
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", professor="
+ professor + "]";
}
}
public class Professor implements Serializable {
private String name;
private int age;
public Professor(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Professor [name=" + name + ", age=" + age + "]";
}
}
public class DeepClone {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Professor p=new Professor("wangwu",50);
Student s1=new Student("zhangsan",18, p);
System.out.println(s1);
Student s2=(Student)s1.deepClone();
s2.getProfessor().setName("maer");
s2.getProfessor().setAge(40);
System.out.println("复制后的:s1 = " + s1);
System.out.println("复制后的:s2 = " + s2);
}
}
介绍完浅克隆和深克隆,再回到原型模式上。
原型模式的UML类图如下:
在原型模式结构图中包含如下几个角色:
public interface Prototype {
Prototype clone();
void setAttr(String attr);
}
public class ConcretePrototype implements Prototype {
private String attr; //成员属性
public void setAttr(String attr) {
this.attr = attr;
}
public String getAttr() {
return this.attr;
}
public Prototype clone() {
Prototype prototype = new ConcretePrototype(); //创建新对象
prototype.setAttr(this.attr);
return prototype;
/*Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException exception) {
System.err.println("Not support cloneable");
}
return (Prototype) object;*/
}
}
clone()方法是不能直接返回this的,相信都明白。
看过仙侠小说的都知道,修炼到高境界(肯定不是练气,金丹的渣渣了),就可以炼制身外化身,这些化身都有自己的思想,可以修炼自己的功法,但却听从主体的命令,当然有时候化身也会叛变,这里就以仙人化身为例:
public class Immortal implements Serializable {
private String name;
private int age;
private String magicalPower;//神通
private Wife wife;//道侣
public Immortal(String name, int age, String magicalPower) {
try {
Thread.sleep(4000);//模拟仙人修炼
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;
this.age = age;
this.magicalPower = magicalPower;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setMagicalPower(String magicalPower) {
this.magicalPower = magicalPower;
}
public void setWife(Wife wife) {
this.wife = wife;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getMagicalPower() {
return magicalPower;
}
public Wife getWife() {
return wife;
}
//使用序列化实现深克隆
public Immortal deepClone() throws IOException, ClassNotFoundException {
//将对象写入流中
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Immortal) ois.readObject();
}
public String toString() {
return "仙人 [姓名=" + name + ", 年龄=" + age + ", 神通="
+ magicalPower + ",道侣=" + wife.getName() + "]";
}
}
public class Wife implements Serializable{
private String name;
private int age;
public Wife(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String toString(){
return "道侣 [姓名=" + name + ", 年龄=" + age + "]";
}
}
public class Client {
public static void main(String[] args) {
Wife yushiqie = new Wife("雨师妾", 3000);
Immortal immortal = new Immortal("拓拔野", 2985, "天元诀,刹那芳华");
immortal.setWife(yushiqie);
System.out.println(immortal);
try {
//故事最后拓跋陪我最爱的雨师妾归隐,但姑射仙子却没了归宿,这里假设拓跋分出一个分身
Immortal incarnation = immortal.deepClone();
Wife guyexianzi = incarnation.getWife();
System.out.println(immortal.getWife() == incarnation.getWife()); //false
guyexianzi.setName("姑射仙子");
guyexianzi.setAge(2985);
System.out.println(incarnation);
incarnation.setWife(guyexianzi);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
原型模式的主要优点如下:
原型模式的主要缺点如下:
在以下情况下可以考虑使用原型模式: