原型模式就是用來生成同樣的對象的!比如你的簡歷,你創建好一份簡歷之後,通過打印機不斷的打印就能創建出多份相同的簡歷出來,這些簡歷內容都是一模一樣的,用程序來表示的話,就是雖然對象不同(內存地址不同),但屬性都是一模一樣...
我們看看如果不用原型模式的話會是什麼樣子:
//一份簡歷
class Resume{
private String name ;//你的姓名
private String phone;//你的聯繫電話
private String email;//你的郵箱
private List<Work> works = new ArrayList<Work>() ;//你的多份工作經歷
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getPhone() {return phone;}
public void setPhone(String phone) {this.phone = phone;}
public String getEmail() {return email;}
public void setEmail(String email) {this.email = email;}
public List<Work> getWorks() {return works;}
public void setWorks(List<Work> works) {this.works = works;}
//爲什麼要這樣打印一下,要打印內存地址,我們接下來就知道了
public void show(){
String res = "當前對象地址:"+this+",引用屬性works地址:"+works+"\r\n"
+"name:"+name+","
+"phone:"+phone+","
+"email:"+email+","
+"works:[\r\n";
for (Work work : works) {
res+=("companyName:"+work.getCompanyName()+",content:"+work.getContent()+"\r\n]");
}
System.out.println(res);
}
}
//工作經歷
class Work{
private String companyName;//公司名稱
private String content;//工作內容
public Work(String companyName, String content) {
this.companyName = companyName;
this.content = content;
}
public String getCompanyName() {return companyName;}
public void setCompanyName(String companyName) {this.companyName = companyName;}
public String getContent() {return content;}
public void setContent(String content) {this.content = content;}
}
public class Test {
public static void main(String[] args) {
//創建一份簡歷
Resume rOne = new Resume();
rOne.setName("段譽");
rOne.setPhone("18359612332");
rOne.setEmail("[email protected]");
rOne.getWorks().add(new Work("阿里巴巴科技公司", "智能助手系統架構..."));
//創建二份簡歷
Resume rTwo = new Resume();
rTwo.setName("段譽");
rTwo.setPhone("18359612332");
rTwo.setEmail("[email protected]");
rTwo.getWorks().add(new Work("阿里巴巴科技公司", "智能助手系統架構..."));
//創建三份簡歷
Resume rThree = new Resume();
rThree.setName("段譽");
rThree.setPhone("18359612332");
rThree.setEmail("[email protected]");
rThree.getWorks().add(new Work("阿里巴巴科技公司", "智能助手系統架構..."));
//還有N份...
//你該不會想直接寫Resume rTwo = rOne;這種代碼吧...拜託,這只是引用...
//好了,現在你的簡歷你要加上學歷...專業...等等內容,怎麼辦?
//得每份簡歷都挨個去加上阿!後面無論是什麼修改,你都要修改N次阿!如此,我們的原型模式就可以派上用場咯!
}
}
輸出:***************************************************************
當前對象地址:Resume@961dff,引用屬性works地址:[Work@18b81e3]
name:段譽,phone:18359612332,email:[email protected],works:[
companyName:阿里巴巴科技公司,content:智能助手系統架構...
]
當前對象地址:Resume@1fc6e42,引用屬性works地址:[Work@1aaf0b3]
name:段譽,phone:18359612332,email:[email protected],works:[
companyName:阿里巴巴科技公司,content:智能助手系統架構...
]
當前對象地址:Resume@1a082e2,引用屬性works地址:[Work@f0c85e]
name:段譽,phone:18359612332,email:[email protected],works:[
companyName:阿里巴巴科技公司,content:智能助手系統架構...
]
**********************************************************************
//一份簡歷,實現克隆接口
class Resume implements Cloneable{
private String name ;//你的姓名
private String phone;//你的聯繫電話
private String email;//你的郵箱
private List<Work> works = new ArrayList<Work>() ;//你的多份工作經歷
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getPhone() {return phone;}
public void setPhone(String phone) {this.phone = phone;}
public String getEmail() {return email;}
public void setEmail(String email) {this.email = email;}
public List<Work> getWorks() {return works;}
public void setWorks(List<Work> works) {this.works = works;}
//覆蓋次克隆方法
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void show(){
String res = "當前對象地址:"+this+",引用屬性works地址:"+works+"\r\n"
+"name:"+name+","
+"phone:"+phone+","
+"email:"+email+","
+"works:[\r\n";
for (Work work : works) {
res+=("companyName:"+work.getCompanyName()+",content:"+work.getContent()+"\r\n]");
}
System.out.println(res);
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
//創建一份簡歷
Resume rOne = new Resume();
rOne.setName("段譽");
rOne.setPhone("18359612332");
rOne.setEmail("[email protected]");
rOne.getWorks().add(new Work("阿里巴巴科技公司", "智能助手系統架構..."));
//使用克隆創建第二份簡歷
Resume rTwo = (Resume) rOne.clone();
//使用克隆創建第三份簡歷
Resume rThree = (Resume) rOne.clone();
rOne.show();
rTwo.show();
rThree.show();
}
}
然而,我們來看一下Test類的輸出,你會發現存在一個問題:
輸出**********************************************************
當前對象地址:Resume@fcfa52,引用屬性works地址:[Work@961dff]
name:段譽,phone:18359612332,email:[email protected],works:[
companyName:阿里巴巴科技公司,content:智能助手系統架構...
]
當前對象地址:Resume@18b81e3,引用屬性works地址:[Work@961dff]
name:段譽,phone:18359612332,email:[email protected],works:[
companyName:阿里巴巴科技公司,content:智能助手系統架構...
]
當前對象地址:Resume@1fc6e42,引用屬性works地址:[Work@961dff]
name:段譽,phone:18359612332,email:[email protected],works:[
companyName:阿里巴巴科技公司,content:智能助手系統架構...
]
**********************************************************
我們可以看到,雖然Resume是一個嶄新的對象(內存地址不同),但它的屬性並不是完全嶄新的,仔細看它的works屬性,這是一個引用屬性,我們可以看到,它的內存地址還是一樣的!這就存在問題了,在某些情況下,可能需要對引用屬性進行操作,此時無論是操作rOne還是rTwo的works,實際上都是在操作同一個對象!那麼這種還存在有同樣引用的克隆方式,我們稱之爲淺克隆,與之相反的是深克隆,既任何屬性都是嶄新的!
要實現深克隆,需要我們改寫clone方法,比如這樣:
//一份簡歷,實現克隆接口
class Resume implements Cloneable{
//省略...
//覆蓋次克隆方法
@Override
public Object clone() throws CloneNotSupportedException {
Resume resume = (Resume) super.clone();
//讓works也構建一份新的
List<Work> newWorks = new ArrayList<Work>();
newWorks.addAll(this.works);
resume.works = newWorks;
return resume;
}
//省略
}
好了,原型模式就這些,它的實現主要依賴於JDK的Cloneable接口,需要注意的是它的淺克隆和深克隆!很簡單吧!