原型模式(Prototype): 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
这里我用Java来给大家介绍一下它的基础使用。
基础示例:
说明:在Java中自定义类的对象可以被复制,自定义类就必须实现Cloneable中的clone()方法,而在.NET中便是实现ICloneable接口其中的唯一方法Clone()方法
原型类
public abstract class Prototype implements Cloneable{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public abstract Prototype Clone() throws CloneNotSupportedException;
}
具体原型类:
public class ConcretePrototype1 extends Prototype {
private String id;
public ConcretePrototype1(String id){
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public Prototype Clone() throws CloneNotSupportedException {
return (Prototype) this.clone();
}
}
客户端代码:
public class UserCilent {
public static void main(String[] args) throws CloneNotSupportedException {
ConcretePrototype1 c1 = new ConcretePrototype1("clonedId");
ConcretePrototype1 c2 = (ConcretePrototype1) c1.Clone();
System.out.println("Cloned id: " + c2.getId());
}
}
结果显示:
Cloned id: clonedId
总结:
1, 原型模式其实是从一个对象再创建另外一个可定制的对象,而且不需要知道其中的具体创建细节,在性能方面,它比传统的 new一个新对象消耗的资源更小。
2, 在不同语言中具体的类的复制需要实现cloneable接口其中的clone()方法才可以进行对象克隆
浅复制与深复制
在clone方法中,(有的编程语言是MemberwiseClone()) : 如果字段是值类型的。则对该字段执行逐位复制,如果字段是引用类型,则只复制引用但不复制引用对象;因此,元素对象及其复本引用同一个对象。具体什么意思呢?在下面的员工类中包含一个部门对象的引用,如果对员工使用clone()方法,只会有里面部门对象引用,而不包含具体的部门对象字段信息。
浅复制
部门类
public class Dept {
private String deptno; //部门号
private String dName; //部门名称
public String getDeptno() {
return deptno;
}
public void setDeptno(String deptno) {
this.deptno = deptno;
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
}
员工类
public class Emp implements Cloneable{
private String ename; //用户名
private String job;
private String sal;
private Dept dept;
public Emp(String ename){
this.ename = ename;
dept = new Dept(); //在 员工实例化时也要实例化部门
}
public void setJobInfo(String job,String sal) {
this.job = job;
this.sal = sal;
}
public void setDept(String deptno,String dName) {
dept.setDeptno(deptno);
dept.setdName(dName);
}
public void Display(){
System.out.println("姓名: " + ename +" ,工作: "+ job + " ,薪水: " +sal);
System.out.println("部门信息: " + dept.getDeptno() + " , " + dept.getdName());
}
public Emp Clone(){
try {
return (Emp) this.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
客户端调用:
public static void main(String[] args) throws CloneNotSupportedException {
Emp emp = new Emp1("张三");
emp.setJobInfo("文员", "5000");
emp.setDept("10", "行政部");
Emp e2 = emp.Clone();
e2.setDept("20", "人力资源部");
Emp e3 = emp.Clone();
e3.setJobInfo("应用开发工程师", "8000");
e3.setDept("30", "信息部");
emp.Display();
e2.Display();
e3.Display();
}
结果显示:
姓名: 张三 ,工作: 文员 ,薪水: 5000
部门信息: 30 , 信息部
姓名: 张三 ,工作: 文员 ,薪水: 5000
部门信息: 30 , 信息部
姓名: 张三 ,工作: 应用开发工程师 ,薪水: 8000
部门信息: 30 , 信息部
浅复制总结:通过结果发现,虽然给emp,e2,e3这三个对象的部门引用设置了不同的部门信息,但是这三个引用都指向最后的那个dept对象。这就叫“浅复制”,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
深复制:
深复制把引用对象的变量指向复制过的新对象,而不是原来的被引用的对象
部门类:多了一个clone()方法,使得该对象的引用实际是每个字段值复制而已。
public class Dept implements Cloneable {
private String deptno; //部门号
private String dName; //部门名称
public String getDeptno() {
return deptno;
}
public void setDeptno(String deptno) {
this.deptno = deptno;
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
public Dept Clone(){
try {
return (Dept) this.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
};
}
员工类:与浅复制区别在于,内部的引用对象用的是clone方法,而自身的clone方法实际是new一个对象
public class Emp implements Cloneable{
private String ename; //用户名
private String job;
private String sal;
private Dept dept;
public Emp(String ename){
this.ename = ename;
dept = new Dept(); //在 员工实例化时也要实例化部门
}
private Emp(Dept dept){
this.dept = dept.Clone();
}
public void setJobInfo(String job,String sal) {
this.job = job;
this.sal = sal;
}
public void setDept(String deptno,String dName) {
dept.setDeptno(deptno);
dept.setdName(dName);
}
public void Display(){
System.out.println("姓名: " + ename +" ,工作: "+ job + " ,薪水: " +sal);
System.out.println("部门信息: " + dept.getDeptno() + " , " + dept.getdName());
}
public Emp Clone(){
Emp emp = new Emp(this.dept);
emp.ename = this.ename;
emp.job = this.job;
emp.sal = this.sal;
return emp;
}
}
客户端代码:
public static void main(String[] args) throws CloneNotSupportedException {
Emp emp = new Emp("张三");
emp.setJobInfo("文员", "5000");
emp.setDept("10", "行政部");
Emp e2 = emp.Clone();
e2.setDept("20", "人力资源部");
Emp e3 = emp.Clone();
e3.setJobInfo("应用开发工程师", "8000");
e3.setDept("30", "信息部");
emp.Display();
e2.Display();
e3.Display();
}
结果显示:
姓名: 张三 ,工作: 文员 ,薪水: 5000
部门信息: 10 , 行政部
姓名: 张三 ,工作: 文员 ,薪水: 5000
部门信息: 20 , 人力资源部
姓名: 张三 ,工作: 应用开发工程师 ,薪水: 8000
部门信息: 30 , 信息部
深复制总结:发现深复制也是把最底层的对象引用使用了clone()方法。而外层选择的是new 一个对象的处理,在性能方面只是优化了最底层的资源消耗,并非是层层优化。