需求:
将一只名字为杰克,性别为公的绵羊克隆10份
要求每只绵羊的属性、性别一致
使用前
package com.myy.design.Prototype.before;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 16:59
*/
public class Sheep {
private String name;
private String sex;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
package com.myy.design.Prototype.before;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 17:03
*
* 需求:将一只名字为杰克,性别为公的绵羊克隆10份
* 要求每只绵羊的属性,性别一致
*
*/
public class Client {
public static void main(String[] args) {
Sheep sheep1 = new Sheep("杰克","公");
Sheep sheep2 = new Sheep("杰克","公");
Sheep sheep3 = new Sheep("杰克","公");
Sheep sheep4 = new Sheep("杰克","公");
Sheep sheep5 = new Sheep("杰克","公");
Sheep sheep6 = new Sheep("杰克","公");
Sheep sheep7 = new Sheep("杰克","公");
Sheep sheep8 = new Sheep("杰克","公");
Sheep sheep9 = new Sheep("杰克","公");
Sheep sheep10 = new Sheep("杰克","公");
}
}
为什么不用for循环,是因为后期可能会用到这十只羊
此时假设我要一只名字为易小名的绵羊,其他绵羊属性与杰克一致
那么按照以上的设计,只能如上创建所需要的绵羊
如上方式的创建,目前只有两个属性问题不大,如果绵羊类的有十几二十甚至更多的属性,那么是非常不方便的Sheep sheep11 = new Sheep("易小名","公")
。
对于编码而言不利于维护。
使用后
实现克隆接口:implements Cloneable 及clone方法
package com.myy.design.Prototype.after;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 16:59
*/
public class Sheep implements Cloneable{
private String name;
private String sex;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
System.out.println("被克隆了...");
return obj;
}
}
package com.myy.design.Prototype.after;
import com.myy.design.Prototype.after.Sheep;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 17:03
*
* 需求:将一只名字为杰克,性别为公的绵羊克隆10份
* 要求每只绵羊的属性,性别一致
*
*/
public class Client {
public static void main(String[] args) throws Exception{
Sheep sheep1 = new Sheep("杰克","公");
Sheep sheep2 = (Sheep) sheep1.clone();
Sheep sheep3 = (Sheep) sheep1.clone();
Sheep sheep4 = (Sheep) sheep1.clone();
Sheep sheep5 = (Sheep) sheep1.clone();
Sheep sheep6 = (Sheep) sheep1.clone();
Sheep sheep7 = (Sheep) sheep1.clone();
Sheep sheep8 = (Sheep) sheep1.clone();
Sheep sheep9 = (Sheep) sheep1.clone();
Sheep sheep10 = (Sheep) sheep1.clone();
// 查看是否是十只不一样的羊
System.out.println(sheep1.hashCode()+sheep1.getName());
System.out.print(sheep2.hashCode()+sheep2.getName());
// 此时假设我要一只名字为易小名的绵羊,其他绵羊属性与杰克一致
// 安装原型设计模式,调用Client类无需查找杰克相同部分的属性,只需要变动差异部分即可
// 这种设计,目前只有两个属性使用起来感觉没多大区别,如果绵羊类有十几二十几甚至更多的属性,那么会非常明显
sheep1.setName("易小名");//其他属性不需要去关注
Sheep sheep11 = (Sheep) sheep1.clone();
System.out.println("第十一只绵羊我要"+sheep11.getName());
}
}
深层次的使用如上,对于编码而言维护性就更好
从对象创建的角度上来说,原型模式设计让相似的类实例创建更加的便捷
浅拷贝
package com.myy.design.Prototype.after;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 16:59
*/
public class Sheep implements Cloneable{
private String name;
private String sex;
private Sheep friend;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", friend=" + friend +
'}';
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
System.out.println("被克隆了...");
return obj;
}
}
浅拷贝特点:对象中实例变量,如果是引用变量,不会重新开辟空间
深拷贝
方式一
package com.myy.design.Prototype.after;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 16:59
*/
public class Sheep implements Cloneable{
private String name;
private String sex;
private Sheep friend;
private Sheep bestFriend;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public Sheep getBestFriend() {
return bestFriend;
}
public void setBestFriend(Sheep bestFriend) {
this.bestFriend = bestFriend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", friend=" + friend +
'}';
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
if ((obj == null)){
return obj;
}else{
// 被克隆出来的绵羊(目前这只绵羊的朋友还是本体的朋友)
Sheep sheep = (Sheep)obj;
// 将本体的朋友也克隆一份出来,给克隆羊
Sheep friend = this.getFriend();
if(friend != null){
sheep.setFriend((Sheep)friend.clone());
}
return sheep;
}
}
}
package com.myy.design.Prototype.after;
import com.myy.design.Prototype.after.Sheep;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 17:03
*
* 需求:将一只名字为杰克,性别为公的绵羊克隆10份
* 要求每只绵羊的属性,性别一致
*
*/
public class Client {
public static void main(String[] args) throws Exception{
Sheep sheep1 = new Sheep("易小名","公");
Sheep friend_yxm =new Sheep("易小名的朋友","母");
Sheep bestFriend_yxm =new Sheep("易小名的好朋友","公");
sheep1.setFriend(friend_yxm);
sheep1.setBestFriend(bestFriend_yxm);
Sheep sheep2 = (Sheep) sheep1.clone();
System.out.println("第一只叫易小名的绵羊的朋友:"+sheep1.getFriend().hashCode());
System.out.println("第二只叫易小名的绵羊的朋友:"+sheep2.getFriend().hashCode());
System.out.println("第一只叫易小名的绵羊的好朋友:"+sheep1.getBestFriend().hashCode());
System.out.println("第二只叫易小名的绵羊的好朋友:"+sheep2.getBestFriend().hashCode());
}
}
方式二
package com.myy.design.Prototype.after;
import java.io.*;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 16:59
*/
public class Sheep implements Cloneable, Serializable {
private String name;
private String sex;
private Sheep friend;
private Sheep bestFriend;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public Sheep getBestFriend() {
return bestFriend;
}
public void setBestFriend(Sheep bestFriend) {
this.bestFriend = bestFriend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", friend=" + friend +
'}';
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
System.out.println("被克隆了...");
return obj;
}
protected Object oneclone() throws CloneNotSupportedException,IOException, ClassNotFoundException {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 获取对象输出流
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 将当前的对象
oos.writeObject(this);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Object obj = ois.readObject();
return obj;
}
}
package com.myy.design.Prototype.after;
import com.myy.design.Prototype.after.Sheep;
/**
* @author 熊贝贝
* @site www.myy.com
* @company
* @create 2020-03-18 17:03
*
* 需求:将一只名字为杰克,性别为公的绵羊克隆10份
* 要求每只绵羊的属性,性别一致
*
*/
public class Client {
public static void main(String[] args) throws Exception{
Sheep sheep1 = new Sheep("易小名","公");
Sheep friend_yxm =new Sheep("易小名的朋友","母");
Sheep bestFriend_yxm =new Sheep("易小名的好朋友","公");
sheep1.setFriend(friend_yxm);
sheep1.setBestFriend(bestFriend_yxm);
Sheep sheep2 = (Sheep) sheep1.oneclone();
System.out.println("第一只叫易小名的绵羊的朋友:"+sheep1.getFriend().hashCode());
System.out.println("第二只叫易小名的绵羊的朋友:"+sheep2.getFriend().hashCode());
System.out.println("第一只叫易小名的绵羊的好朋友:"+sheep1.getBestFriend().hashCode());
System.out.println("第二只叫易小名的绵羊的好朋友:"+sheep2.getBestFriend().hashCode());
}
}
推荐使用深拷贝的第二种方式
注意事项和细节
- 创建新的对象比较复杂时,可以利用原型模式简化对象创建过程,同时也能够提高效率
- 不能重新初始化对象,而动态地获得对象运行时的状态
- 如果原始对象发生变化(增加或者减少属性),其他克隆对象也会发生相应的变化,无需修改代码
- 与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流
优点:
- 性能提高。
- 逃避构造函数的约束
缺点:
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
- 必须实现 Cloneable 接口。
使用场景:
- 资源优化场景。
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 性能和安全要求的场景。
- 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
- 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。