java設(shè)計(jì)模式:原始模型模式
什么是原始模型模式
通過給出一個(gè)原型對象指明所要創(chuàng)建的對象的類型,然后通過復(fù)制這個(gè)原型對象來獲取的更多的同類型的對象。
這讓我不由自主的想起克隆技術(shù),還記得克隆羊嗎?我們接下來講的內(nèi)容和克隆羊不能說關(guān)系密切,只能說毫無關(guān)系。

設(shè)計(jì)模式和編程語言無關(guān),但是二當(dāng)家的依然用Java語言去實(shí)戰(zhàn)舉例。而且Java有標(biāo)準(zhǔn)的實(shí)現(xiàn)原始模型模式的方法。
原始模型模式中的角色

Prototype:抽象類或者一個(gè)接口,給出具體模型需要的接口。ConcretePrototype:繼承抽象原型模型角色,被復(fù)制的對象。Client:提出復(fù)制請求。
抽象原型角色(Prototype)
我們用家庭作業(yè)為抽象原型角色(Prototype)。我們這里的作業(yè)是可以抄的。大家不要學(xué)哈。
package com.secondgod.prototype;
/**
* 作業(yè)
*
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
*/
public interface IHomework {
/**
* 抄一份
* @return
*/
IHomework copy();
/**
* 修改所有者
* @param owner
*/
void setOwner(String owner);
}
具體原型角色(ConcretePrototype)
我們用語文作業(yè)作為具體原型角色(ConcretePrototype)。
package com.secondgod.prototype;
import java.text.MessageFormat;
/**
* 語文作業(yè)
*
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
*/
public class ChineseHomework implements IHomework {
/**
* 作業(yè)的所有者
*/
private String owner;
/**
* 作業(yè)標(biāo)題/作業(yè)要求
*/
private String title;
/**
* 作業(yè)內(nèi)容
*/
private String content;
public ChineseHomework(String owner, String title, String content) {
this.owner = owner;
this.title = title;
this.content = content;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String toString() {
return MessageFormat.format("owner:{0},title:{1},content:{2}", owner, title, content);
}
@Override
public IHomework copy() {
ChineseHomework homework = new ChineseHomework(this.getOwner(), this.getTitle(), this.getContent());
return homework;
}
}
客戶端角色(Client)
我們測試一下。
package com.secondgod.prototype;
/**
* 測試原始模型
*
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Test {
public static void main(String[] args) {
// 老師讓寫作業(yè),大當(dāng)家按時(shí)完成
IHomework homework = new ChineseHomework("大當(dāng)家的", "作文-最崇拜的人", "不瞞你們說,我最崇拜的是二當(dāng)家的");
// 二當(dāng)家的沒按時(shí)完成,決定去抄大當(dāng)家的作業(yè)~
IHomework newHomework = homework.copy();
newHomework.setOwner("二當(dāng)家的");
System.out.println(homework);
System.out.println(newHomework);
}
}

和我們的預(yù)期一致
使用Java內(nèi)置機(jī)制實(shí)現(xiàn)原始模型模式
在Object類中有這樣一個(gè)方法,Java中所有的類都繼承自O(shè)bject類,也就是說所有的類內(nèi)部都可以復(fù)制自己。但是卻不能直接調(diào)用別的類的克隆方法。也就是說有統(tǒng)一的方式,但是默認(rèn)不可用。

我們改一下語文作業(yè)類,使用clone方法去嘗試,克隆自己。
package com.secondgod.prototype;
import java.text.MessageFormat;
/**
* 語文作業(yè)
*
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
*/
public class ChineseHomework implements IHomework {
/**
* 作業(yè)的所有者
*/
private String owner;
/**
* 作業(yè)標(biāo)題/作業(yè)要求
*/
private String title;
/**
* 作業(yè)內(nèi)容
*/
private String content;
public ChineseHomework(String owner, String title, String content) {
this.owner = owner;
this.title = title;
this.content = content;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String toString() {
return MessageFormat.format("owner:{0},title:{1},content:{2}", owner, title, content);
}
@Override
public IHomework copy() {
try {
return (ChineseHomework) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}

這時(shí)候會報(bào)錯(cuò),因?yàn)槲覀冞€少做一件事。我們需要把語文作業(yè)類實(shí)現(xiàn)Cloneable接口,然而這個(gè)接口里沒有任何抽象方法,僅僅是一個(gè)標(biāo)記接口。這就像注解一樣,就是為了明確聲明可以克隆。

實(shí)現(xiàn)接口后,則可以正確運(yùn)行。
package com.secondgod.prototype;
import java.text.MessageFormat;
/**
* 語文作業(yè)
*
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
*/
public class ChineseHomework implements IHomework, Cloneable {
/**
* 作業(yè)的所有者
*/
private String owner;
/**
* 作業(yè)標(biāo)題/作業(yè)要求
*/
private String title;
/**
* 作業(yè)內(nèi)容
*/
private String content;
public ChineseHomework(String owner, String title, String content) {
this.owner = owner;
this.title = title;
this.content = content;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String toString() {
return MessageFormat.format("owner:{0},title:{1},content:{2}", owner, title, content);
}
@Override
public IHomework copy() {
try {
return (ChineseHomework) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}

和我們自己實(shí)現(xiàn)copy效果一樣。clone是一個(gè)native方法,是交給本地實(shí)現(xiàn)的,通常是直接內(nèi)存拷貝。
淺拷貝和深拷貝
淺拷貝:創(chuàng)建一個(gè)新對象,然后將當(dāng)前對象的非靜態(tài)字段復(fù)制到該新對象,如果字段是值類型的,那么對該字段執(zhí)行復(fù)制;如果該字段是引用類型的話,則復(fù)制引用但不復(fù)制引用的對象。因此,原始對象及其副本引用同一個(gè)對象。
深拷貝:創(chuàng)建一個(gè)新對象,然后將當(dāng)前對象的非靜態(tài)字段復(fù)制到該新對象,無論該字段是值類型的還是引用類型,都復(fù)制獨(dú)立的一份。當(dāng)你修改其中一個(gè)對象的任何內(nèi)容時(shí),都不會影響另一個(gè)對象的內(nèi)容。
二當(dāng)家的理解方式是,淺拷貝就是僅拷貝當(dāng)前對象的內(nèi)容,深拷貝就是遞歸拷貝當(dāng)前對象和當(dāng)前對象的引用類型屬性的內(nèi)容,直到全部都是基本數(shù)據(jù)類型的屬性為止。另外,僅僅使用clone方法是淺拷貝。
package com.secondgod.prototype;/** * 測試原始模型 * * @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/ */public class Test implements Cloneable { private Field field; public Field getField() { return field; } public void setField(Field field) { this.field = field; } public Test clone() throws CloneNotSupportedException { return (Test) super.clone(); } public static void main(String[] args) throws CloneNotSupportedException { Test t = new Test(); t.setField(new Field()); Test cloneT = t.clone(); System.out.println(t == cloneT); System.out.println(t.getField() == cloneT.getField()); }}class Field {}

源對象和克隆出的新對象field屬性值是同一個(gè)對象。所以是淺拷貝。
怎么實(shí)現(xiàn)深拷貝
讓每個(gè)引用類型屬性內(nèi)部都重寫clone() 方法,然后需要對所有引用類型屬性都調(diào)用clone方法。
package com.secondgod.prototype;
/**
* 測試原始模型
*
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Test implements Cloneable {
private Field field;
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
public Test clone() throws CloneNotSupportedException {
return (Test) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Test t = new Test();
t.setField(new Field());
Test cloneT = t.clone();
System.out.println(t == cloneT);
System.out.println(t.getField() == cloneT.getField());
}
}
class Field {
}

這種方式需要遞歸每個(gè)引用類型的屬性,要把他們的類都實(shí)現(xiàn)深拷貝,只到僅有基本數(shù)據(jù)類型為止。
利用序列化,這是個(gè)偷懶的辦法。但是二當(dāng)家的覺得更安全,如果你確定是要深拷貝,使用該方式可以防止某處漏實(shí)現(xiàn)clone方法,而變成不完全的深拷貝。
package com.secondgod.prototype;
/**
* 測試原始模型
*
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
*/
public class Test implements Cloneable {
private Field field;
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
public Test clone() throws CloneNotSupportedException {
return (Test) super.clone();
}
public Test deepClone() throws CloneNotSupportedException {
Test t = new Test();
t.setField(this.getField().deepClone());
return t;
}
public static void main(String[] args) throws CloneNotSupportedException {
Test t = new Test();
t.setField(new Field());
Test cloneT = t.clone();
System.out.println(t == cloneT);
System.out.println(t.getField() == cloneT.getField());
Test deepCloneT = t.deepClone();
System.out.println(t == deepCloneT);
System.out.println(t.getField() == deepCloneT.getField());
}
}
class Field implements Cloneable {
public Field clone() throws CloneNotSupportedException {
return (Field) super.clone();
}
public Field deepClone() throws CloneNotSupportedException {
// 沒有引用類型屬性
return this.clone();
}
}

到底使用淺拷貝還是深拷貝,要根據(jù)實(shí)際情況。但是有一點(diǎn)是確定的,深拷貝需要創(chuàng)建更多對象,占用更多內(nèi)存。
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
基于mybatis注解動態(tài)sql中foreach工具的方法
這篇文章主要介紹了mybatis注解動態(tài)sql中foreach工具方法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
spring無法引入注解及import org.springframework.web.bind.annota
本文主要介紹了spring無法引入注解及import org.springframework.web.bind.annotation.*報(bào)錯(cuò)的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
Java中的CompletionService批量異步執(zhí)行詳解
這篇文章主要介紹了Java中的CompletionService批量異步執(zhí)行詳解,我們知道線程池可以執(zhí)行異步任務(wù),同時(shí)可以通過返回值Future獲取返回值,所以異步任務(wù)大多數(shù)采用ThreadPoolExecutor+Future,需要的朋友可以參考下2023-12-12
詳細(xì)總結(jié)Java for循環(huán)的那些坑
在平常寫代碼的過程中循環(huán)是不可避免的,雖然for的語法并不復(fù)雜,但是在開發(fā)中還是會遇到一些坑,雖然大部分的坑都是自己的騷操作導(dǎo)致的.今天來總結(jié)一下for循環(huán)在開發(fā)中可能遇到的坑,不要在同樣的問題上再次犯錯(cuò).需要的朋友可以參考下2021-05-05
java多線程CyclicBarrier的使用案例,讓線程起步走
這篇文章主要介紹了java多線程CyclicBarrier的使用案例,讓線程起步走!具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
SpringBoot對數(shù)據(jù)訪問層進(jìn)行單元測試的方法詳解
我們公司作為一個(gè)面向銀行、金融機(jī)構(gòu)的TO B類企業(yè),頻繁遇到各個(gè)甲方爸爸提出的國產(chǎn)化數(shù)據(jù)庫的改造需求,包括OceanBase, TiDB,geldenDB等等,本文就介紹一種快高效、可復(fù)用的解決方案——對數(shù)據(jù)訪問層做單元測試,需要的朋友可以參考下2023-08-08

