概念
原型模式(Prototype pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,属于创建者模式
原型模式主要包含三个角色:
- 客户(client):客户类提供创建对象的请求
- 抽象原型(prototype):规定拷贝接口
- 具体原型(concrete prototype):被拷贝的对象
注意:对不通过new关键字,而是通过对象拷贝来实现创建对象的模式就称为原型模式
实现
浅克隆
标准写法
1.创建原型接口
public interface IPrototype<T> {
T clone();
}
2.创建具体原型
public class ConcretePrototype implements IPrototype {
int age;
String name;
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public ConcretePrototype clone() {
ConcretePrototype cp=new ConcretePrototype();
cp.setAge(this.age);
cp.setName(this.name);
return cp;
}
}
3.测试
public class PrototypeTest {
public static void main(String[] args) {
//创建原型
ConcretePrototype cp =new ConcretePrototype();
cp.setName("tom");
cp.setAge(21);
System.out.println(cp);
//拷贝原型
ConcretePrototype cloneType=cp.clone();
System.out.println(cloneType);
}
}
运行结果:
这时候,有人就问了,这不就是把属性复制放到对象里面么,和原来也没多少差别。确实,上面的赋值过程是我们自己完成的,在实际编码中,我们一般不会浪费做这种浪费体力的劳动。
接下来,我们看原型模式另一种写法
实现JDK中的Cloneable接口
JDK已经帮我们实现了一个线程API,我们只需实现Cloneable接口接口即可
1.创建具体原型
public class ConcretePrototype implements Cloneable {
int age;
String name;
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public ConcretePrototype clone() {
try {
return (ConcretePrototype)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
2.测试
public class PrototypeTest {
public static void main(String[] args) {
//创建原型
ConcretePrototype cp =new ConcretePrototype();
cp.setName("tom");
cp.setAge(21);
System.out.println(cp);
//拷贝原型
ConcretePrototype cloneType=cp.clone();
System.out.println(cloneType);
}
}
运行结果:
接下来,我们在ConcretePrototype 类中添加爱好属性:
public class PrototypeTest {
public static void main(String[] args) {
//创建原型
ConcretePrototype cp =new ConcretePrototype();
cp.setName("tom");
cp.setAge(21);
List<String>hobbies=new ArrayList<String>();
hobbies.add("书法");
hobbies.add("绘画");
cp.setHobbies(hobbies);
//拷贝原型
ConcretePrototype cloneType=cp.clone();
cloneType.getHobbies().add("游泳");
System.out.println(cp);
System.out.println(cloneType);
}
}
运行结果:
我们在给复制对象添加了一项爱好之后发现,原型对象也跟着发生了改变。这显然不符合我们的预期,因为我们希望克隆出来的对象与原型对象之间是两个独立对象,没有任何联系。经过分析发现,原来是hobbies共用了一个地址,也就是说,复制的是引用的地址。这个也就是我们常说的浅克隆
深克隆
通过序列化方式实现:
public class ConcretePrototype implements Serializable {
int age;
String name;
List<String>hobbies;
@Override
public String toString() {
return "ConcretePrototype{" +
"age=" + age +
", name='" + name + '\'' +
", hobbies=" + hobbies +
'}';
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ConcretePrototype deepClone() {
try {
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
return (ConcretePrototype)ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class PrototypeTest {
public static void main(String[] args) {
//创建原型
ConcretePrototype cp =new ConcretePrototype();
cp.setName("tom");
cp.setAge(21);
List<String>hobbies=new ArrayList<String>();
hobbies.add("书法");
hobbies.add("绘画");
cp.setHobbies(hobbies);
//拷贝原型
ConcretePrototype cloneType=cp.deepClone();
cloneType.getHobbies().add("游泳");
System.out.println(cp);
System.out.println(cloneType);
}
}
运行结果:
深克隆会破坏单例,解决方法也非常简单:禁止深克隆即可。要么我们的单例类不实现Cloneable接口;要么我们从写==clone()方法,在clone()==方法中返回单例对象即可。
在源码中的运用
我们先看Cloneable 接口:
public interface Cloneable {
}
Cloneable 接口很简单,再看一下它的实现类。比如ArrayList类的实现:
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
总结
优点
- 提高了性能。比直接new一个对象性能上提升了许多
- 简化了创建对象的过程
缺点
- 需要为每一个类配置一个克隆方法
- 当对已有的类进行改造时,需要修改代码,违背了开闭原则
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/12912.html