第十四篇:原型模式

原型模式就是用來生成同樣的對象的!比如你的簡歷,你創建好一份簡歷之後,通過打印機不斷的打印就能創建出多份相同的簡歷出來,這些簡歷內容都是一模一樣的,用程序來表示的話,就是雖然對象不同(內存地址不同),但屬性都是一模一樣...

我們看看如果不用原型模式的話會是什麼樣子:

//一份簡歷
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();
	}
}


我們看到,簡歷類實現了我們的Cloneable接口並且覆蓋了clone()方法,現在我們的Test類不再需要多次創建對象了,只需要調用原生對象的clone()方法,就可以完整的克隆出一個新的對象!
然而,我們來看一下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接口,需要注意的是它的淺克隆和深克隆!很簡單吧!





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章