一文帶你了解Java創(chuàng)建型設(shè)計(jì)模式之原型模式
原型模式
概述
原型模式(Prototype Pattern)是屬于創(chuàng)建型模式。
它指用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過拷貝這些原型創(chuàng)建新的對(duì)象。
原型模式的核心在于拷貝原型對(duì)象。以存在的一個(gè)對(duì)象為原型,直接基于內(nèi)存二進(jìn)制流進(jìn)行拷貝,無需再經(jīng)歷耗時(shí)的對(duì)象初始化過程(不調(diào)用構(gòu)造函數(shù)),性能提升許多。
當(dāng)直接創(chuàng)建對(duì)象的代價(jià)比較大時(shí),則采用這種模式。利用當(dāng)前系統(tǒng)中已存在的對(duì)象作為原型,對(duì)其進(jìn)行克隆,避免初始化的過程。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1.java自帶的原型模式是基于內(nèi)存二進(jìn)制流的拷貝,比直接new一個(gè)對(duì)象性能上提升許多
2.使用原型模式將對(duì)象復(fù)制一份并將其狀態(tài)保存起來,簡(jiǎn)化創(chuàng)建對(duì)象的過程,以便在需要的時(shí)候使用
缺點(diǎn):
1.需要為每一個(gè)類配置一個(gè)克隆方法
2克隆方法位于類的內(nèi)部,當(dāng)對(duì)已有類進(jìn)行改造的時(shí)候,需要修改代碼,違反開閉原測(cè)
3.在實(shí)現(xiàn)深克隆時(shí)需要編寫較為復(fù)雜的代碼,而且當(dāng)對(duì)象之間存在多重嵌套引用時(shí),為了實(shí)現(xiàn)深克隆,每一層對(duì)象對(duì)應(yīng)的類都必須支特深克隆,實(shí)現(xiàn)比較麻煩
應(yīng)用場(chǎng)景
1.類初始化消耗資源較多
2.new產(chǎn)生的一個(gè)對(duì)象需要非常繁瑣的過程(數(shù)據(jù)準(zhǔn)備或訪問權(quán)限)
3.構(gòu)造函數(shù)處比較復(fù)雜
4.循環(huán)體中生產(chǎn)大量對(duì)象時(shí)
5.資源優(yōu)化場(chǎng)景
6.性能和安全要求的場(chǎng)景
主要角色
1.客戶(Client)角色
客戶端類向原型管理器提出創(chuàng)建對(duì)象的請(qǐng)求。
2.抽象原型(Prototype)角色
這是一個(gè)抽象角色,此角色給出所有的具體原型類所需的接口。
3.具體原型(Concrete Prototype)角色
被復(fù)制的對(duì)象。此角色需要實(shí)現(xiàn)抽象的原型角色所要求的接口。
4.原型管理器(Prototype Manager)角色
創(chuàng)建具體原型類的對(duì)象,并記錄每一個(gè)被創(chuàng)建的對(duì)象。
原型模式的基本使用
創(chuàng)建原型接口
public interface IPrototype<T> { T clone(); }
創(chuàng)建具體需要克隆對(duì)象
@Data @ToString public class ConcretePrototype implements IPrototype { private int age; private String name; @Override public ConcretePrototype clone() { ConcretePrototype concretePrototype = new ConcretePrototype(); concretePrototype.setAge(this.age); concretePrototype.setName(this.name); return concretePrototype; } }
使用
public static void main(String[] args) { //創(chuàng)建原型對(duì)象 ConcretePrototype prototype = new ConcretePrototype(); prototype.setAge(20); prototype.setName("jack"); System.out.println(prototype); //拷貝原型對(duì)象 ConcretePrototype cloneType = prototype.clone(); System.out.println(cloneType); }
JDK自帶原型接口的使用
原型模式就是如此簡(jiǎn)單,通常開發(fā)中不會(huì)這樣使用。其實(shí)JDK提供了一個(gè)現(xiàn)成的API接口,那就是Cloneable接口。
@Data @ToString public class ConcretePrototype implements Cloneable { private int age; private String name; private List<String> hobbies; @Override public ConcretePrototype clone() { try { return (ConcretePrototype)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } }
淺克隆與深度克隆
淺克隆
添加一個(gè)hobby屬性,當(dāng)給克隆對(duì)象的hobby屬性添加一項(xiàng)時(shí),最終結(jié)果會(huì)導(dǎo)致原型對(duì)象發(fā)生變化,也就是hobby屬性用于一個(gè)內(nèi)存地址。這就是淺克隆。
@Data @ToString public class ConcretePrototype implements Cloneable { private int age; private String name; private List<String> hobby; @Override public ConcretePrototype clone() { try { return (ConcretePrototype)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } }
public static void main(String[] args) { ConcretePrototype prototype = new ConcretePrototype(); prototype.setAge(22); prototype.setName("jack"); List<String> hobby = new ArrayList<String>(); hobby.add("java"); hobby.add("python"); hobby.add("go"); prototype.setHobby(hobby); // 拷貝原型對(duì)象 ConcretePrototype cloneType = prototype.clone(); cloneType.getHobby().add("php"); System.out.println("原型對(duì)象:" + prototype); System.out.println("克隆對(duì)象:" + cloneType); System.out.println(prototype == cloneType); System.out.println(prototype.getHobby() == cloneType.getHobby()); }
原型對(duì)象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
克隆對(duì)象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
false
true
深度克隆
可使用序列化方式實(shí)現(xiàn)對(duì)象的深度克隆
@Data @ToString public class ConcretePrototype implements Cloneable, Serializable { private int age; private String name; private List<String> hobby; @Override public ConcretePrototype clone() { try { return (ConcretePrototype) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } 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 static void main(String[] args) { //創(chuàng)建原型對(duì)象 ConcretePrototype prototype = new ConcretePrototype(); prototype.setAge(22); prototype.setName("jack"); List<String> hobby = new ArrayList<String>(); hobby.add("java"); hobby.add("python"); hobby.add("go"); prototype.setHobby(hobby); // 拷貝原型對(duì)象 ConcretePrototype cloneType = prototype.deepClone(); cloneType.getHobby().add("php"); System.out.println("原型對(duì)象:" + prototype); System.out.println("克隆對(duì)象:" + cloneType); System.out.println(prototype == cloneType); System.out.println(prototype.getHobby() == cloneType.getHobby()); }
原型對(duì)象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go])
克隆對(duì)象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
false
false
也可這樣操作,多克隆一次
public ConcretePrototype deepClone2() { try { ConcretePrototype result = (ConcretePrototype) super.clone(); result.hobby = (List) ((ArrayList) result.hobby).clone(); return result; } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } }
單例的破壞
如果克隆的目標(biāo)對(duì)象是單例對(duì)象,深克隆就會(huì)破壞單例。
解決方案:禁止深克隆。
1.讓單例類不實(shí)現(xiàn)Cloneable接口
2.重寫clone方法,在clone方法中返回單例對(duì)象即可
@Data @ToString public class ConcretePrototype implements Cloneable { private static ConcretePrototype instance = new ConcretePrototype(); private ConcretePrototype(){} public static ConcretePrototype getInstance(){ return instance; } @Override public ConcretePrototype clone() { return instance; } }
到此這篇關(guān)于一文帶你了解Java創(chuàng)建型設(shè)計(jì)模式之原型模式的文章就介紹到這了,更多相關(guān)Java原型模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)網(wǎng)頁驗(yàn)證碼功能
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)網(wǎng)頁驗(yàn)證碼功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Java實(shí)現(xiàn)新建有返回值的線程的示例詳解
本文主要介紹了一個(gè)Java多線程的例題,題目是:使用ThreadLocal管理一號(hào)和二號(hào)線程,分別存入100元,在三號(hào)線程中使用利用一號(hào)和二號(hào)的計(jì)算結(jié)果來算出賬戶的實(shí)際金額。感興趣的可以了解一下2022-09-09使用Autowired為什么會(huì)被IDEA警告最佳修改方法
這篇文章主要介紹了使用Autowired為什么會(huì)被IDEA警告,應(yīng)該怎么修改最佳,除了使用@Autowired以外,我們其實(shí)也有幾種好用的方式,使用@Resource替代@Autiwired方法是其中一種,只需要改變一個(gè)注解,這里就不展示了,需要的朋友可以參考下2023-02-02Java避免UTF-8的csv文件打開中文出現(xiàn)亂碼的方法
這篇文章主要介紹了Java避免UTF-8的csv文件打開中文出現(xiàn)亂碼的方法,結(jié)合實(shí)例形式分析了java操作csv文件時(shí)使用utf-16le編碼與utf8編碼相關(guān)操作技巧,需要的朋友可以參考下2019-07-07