一文帶你了解Java創(chuàng)建型設(shè)計模式之原型模式
原型模式
概述
原型模式(Prototype Pattern)是屬于創(chuàng)建型模式。
它指用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。
原型模式的核心在于拷貝原型對象。以存在的一個對象為原型,直接基于內(nèi)存二進制流進行拷貝,無需再經(jīng)歷耗時的對象初始化過程(不調(diào)用構(gòu)造函數(shù)),性能提升許多。
當直接創(chuàng)建對象的代價比較大時,則采用這種模式。利用當前系統(tǒng)中已存在的對象作為原型,對其進行克隆,避免初始化的過程。
優(yōu)缺點
優(yōu)點:
1.java自帶的原型模式是基于內(nèi)存二進制流的拷貝,比直接new一個對象性能上提升許多
2.使用原型模式將對象復制一份并將其狀態(tài)保存起來,簡化創(chuàng)建對象的過程,以便在需要的時候使用
缺點:
1.需要為每一個類配置一個克隆方法
2克隆方法位于類的內(nèi)部,當對已有類進行改造的時候,需要修改代碼,違反開閉原測
3.在實現(xiàn)深克隆時需要編寫較為復雜的代碼,而且當對象之間存在多重嵌套引用時,為了實現(xiàn)深克隆,每一層對象對應的類都必須支特深克隆,實現(xiàn)比較麻煩
應用場景
1.類初始化消耗資源較多
2.new產(chǎn)生的一個對象需要非常繁瑣的過程(數(shù)據(jù)準備或訪問權(quán)限)
3.構(gòu)造函數(shù)處比較復雜
4.循環(huán)體中生產(chǎn)大量對象時
5.資源優(yōu)化場景
6.性能和安全要求的場景
主要角色
1.客戶(Client)角色
客戶端類向原型管理器提出創(chuàng)建對象的請求。
2.抽象原型(Prototype)角色
這是一個抽象角色,此角色給出所有的具體原型類所需的接口。
3.具體原型(Concrete Prototype)角色
被復制的對象。此角色需要實現(xiàn)抽象的原型角色所要求的接口。
4.原型管理器(Prototype Manager)角色
創(chuàng)建具體原型類的對象,并記錄每一個被創(chuàng)建的對象。
原型模式的基本使用
創(chuàng)建原型接口
public interface IPrototype<T> {
T clone();
}
創(chuàng)建具體需要克隆對象
@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)建原型對象
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAge(20);
prototype.setName("jack");
System.out.println(prototype);
//拷貝原型對象
ConcretePrototype cloneType = prototype.clone();
System.out.println(cloneType);
}
JDK自帶原型接口的使用
原型模式就是如此簡單,通常開發(fā)中不會這樣使用。其實JDK提供了一個現(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;
}
}
}
淺克隆與深度克隆
淺克隆
添加一個hobby屬性,當給克隆對象的hobby屬性添加一項時,最終結(jié)果會導致原型對象發(fā)生變化,也就是hobby屬性用于一個內(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);
// 拷貝原型對象
ConcretePrototype cloneType = prototype.clone();
cloneType.getHobby().add("php");
System.out.println("原型對象:" + prototype);
System.out.println("克隆對象:" + cloneType);
System.out.println(prototype == cloneType);
System.out.println(prototype.getHobby() == cloneType.getHobby());
}
原型對象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
克隆對象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go, php])
false
true
深度克隆
可使用序列化方式實現(xiàn)對象的深度克隆
@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)建原型對象
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);
// 拷貝原型對象
ConcretePrototype cloneType = prototype.deepClone();
cloneType.getHobby().add("php");
System.out.println("原型對象:" + prototype);
System.out.println("克隆對象:" + cloneType);
System.out.println(prototype == cloneType);
System.out.println(prototype.getHobby() == cloneType.getHobby());
}
原型對象:ConcretePrototype(age=22, name=jack, hobby=[java, python, go])
克隆對象: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;
}
}
單例的破壞
如果克隆的目標對象是單例對象,深克隆就會破壞單例。
解決方案:禁止深克隆。
1.讓單例類不實現(xiàn)Cloneable接口
2.重寫clone方法,在clone方法中返回單例對象即可
@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è)計模式之原型模式的文章就介紹到這了,更多相關(guān)Java原型模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java避免UTF-8的csv文件打開中文出現(xiàn)亂碼的方法
這篇文章主要介紹了Java避免UTF-8的csv文件打開中文出現(xiàn)亂碼的方法,結(jié)合實例形式分析了java操作csv文件時使用utf-16le編碼與utf8編碼相關(guān)操作技巧,需要的朋友可以參考下2019-07-07

