原型(Prototype)模式也是解決對象創建常見場景的一種手段,理解起來非常簡單,其核心是clone方法,可以圍繞着拷貝(淺拷貝+深拷貝)以及序列化等場景進行考慮和展開。
模式場景與說明
類的初始化消耗硬件或者其他資源過多,或者對象準備非常繁瑣但是同時有大量重複性的設定等場景,簡單來說當我們創建一個對象的時候希望使用copy命令來完成的時候(當然不同語言中的實現是不同的,此處說的是期待的特性),這個時候就是原型模式起作用的時候了。
實現方式
在Java中,由於已經提供了Cloneable接口,實現起來更加簡單,Java的原型模式和語言本身已經結合的比較緊密,Java的Cloneable接口是java.lang包下的一個接口,如果確認其內容,會看到如下信息
public interface Cloneable {
}
實現示例
示例代碼:淺拷貝
class Graphics {
private String brand;
private String volume;
public Graphics(String brand, String volume) {
this.brand = brand;
this.volume = volume;
System.out.println("Graphics: construct function called.");
}
public void setBrand(String brand) {
this.brand = brand;
}
public void setVolume(String volume) {
this.volume = volume;
}
public String getBrand() {
return brand;
}
public String getVolume() {
return volume;
}
@Override
public String toString(){
return this.brand + " " + this.volume;
}
}
class Computer implements Cloneable {
private String cpu;
private String memory;
private Graphics graphics;
private String motherboard;
public Computer(String cpu, String memory, Graphics graphics, String motherboard) {
this.cpu = cpu;
this.memory = memory;
this.graphics = graphics;
this.motherboard = motherboard;
System.out.println("Computer: construct function called.");
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setMemory(String memory) {
this.memory = memory;
}
public void setGraphics(Graphics graphics) {
this.graphics = graphics;
}
public void setMotherboard(String motherboard) {
this.motherboard = motherboard;
}
public String getCpu() {
return cpu;
}
public String getMemory() {
return memory;
}
public Graphics getGraphics() {
return graphics;
}
public String getMotherboard() {
return motherboard;
}
@Override
protected Object clone(){
Computer computer = null;
try {
computer = (Computer) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return computer;
}
@Override
public String toString(){
return "Computer information: \n"
+ "CPU: " + this.cpu + "\n"
+ "Memory: " + this.memory + "\n"
+ "Graphics: " + this.graphics + "\n"
+ "Motherboard: " + this.motherboard;
}
}
public class TestPrototype {
public static void main(String[] args) {
Computer computer1 = new Computer("2.5 GHz Quad-Core Intel Core i7", "16 GB 1600 MHz DDR3",
new Graphics("Intel Iris Pro","1536 MB"),"820-00xxx");
System.out.println("clone begins ...");
Computer computer2 = (Computer) computer1.clone();
System.out.println("clone ends ...");
System.out.println("computer1.equals(computer2): " + computer1.equals(computer2));
System.out.println("computer1 == computer2: " + (computer1 == computer2));
System.out.println("computer1.getClass() == computer2.getClass(): " + (computer1.getClass() == computer2.getClass()));
System.out.println("set memory of computer 1:");
computer1.setMemory("32G");
System.out.println(computer1);
System.out.println(computer2);
System.out.println("set Graphics of computer 2:");
computer2.getGraphics().setVolume("3072 MB");
System.out.println(computer1);
System.out.println(computer2);
}
}
執行結果
Graphics: construct function called.
Computer: construct function called.
clone begins ...
clone ends ...
computer1.equals(computer2): false
computer1 == computer2: false
computer1.getClass() == computer2.getClass(): true
set memory of computer 1:
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 32G
Graphics: Intel Iris Pro 1536 MB
Motherboard: 820-00xxx
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 16 GB 1600 MHz DDR3
Graphics: Intel Iris Pro 1536 MB
Motherboard: 820-00xxx
set Graphics of computer 2:
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 32G
Graphics: Intel Iris Pro 3072 MB
Motherboard: 820-00xxx
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 16 GB 1600 MHz DDR3
Graphics: Intel Iris Pro 3072 MB
Motherboard: 820-00xxx
結果分析
從上述示例代碼的執行結果中可以看到,又如下特點:
- 執行clone時並未觸發構建函數
- 對於基本類型的修改並不影響副本的值
- 嵌套類的拷貝上述示例僅實現引用程度的實現,修改其中之一,其副本也會收到影響,這就是所謂的淺拷貝和深拷貝
示例代碼:深拷貝
對於其引用的對象也實現Cloneable接口,並在clone實現中加入處理,將代碼修改如下:
class Graphics implements Cloneable{
private String brand;
private String volume;
public Graphics(String brand, String volume) {
this.brand = brand;
this.volume = volume;
System.out.println("Graphics: construct function called.");
}
public void setBrand(String brand) {
this.brand = brand;
}
public void setVolume(String volume) {
this.volume = volume;
}
public String getBrand() {
return brand;
}
public String getVolume() {
return volume;
}
@Override
protected Object clone(){
Graphics graphics = null;
try {
graphics = (Graphics) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return graphics;
}
@Override
public String toString(){
return this.brand + " " + this.volume;
}
}
class Computer implements Cloneable {
private String cpu;
private String memory;
private Graphics graphics;
private String motherboard;
public Computer(String cpu, String memory, Graphics graphics, String motherboard) {
this.cpu = cpu;
this.memory = memory;
this.graphics = graphics;
this.motherboard = motherboard;
System.out.println("Computer: construct function called.");
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setMemory(String memory) {
this.memory = memory;
}
public void setGraphics(Graphics graphics) {
this.graphics = graphics;
}
public void setMotherboard(String motherboard) {
this.motherboard = motherboard;
}
public String getCpu() {
return cpu;
}
public String getMemory() {
return memory;
}
public Graphics getGraphics() {
return graphics;
}
public String getMotherboard() {
return motherboard;
}
@Override
protected Object clone(){
Computer computer = null;
try {
computer = (Computer) super.clone();
computer.setGraphics((Graphics) this.getGraphics().clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return computer;
}
@Override
public String toString(){
return "Computer information: \n"
+ "CPU: " + this.cpu + "\n"
+ "Memory: " + this.memory + "\n"
+ "Graphics: " + this.graphics + "\n"
+ "Motherboard: " + this.motherboard;
}
}
public class TestPrototype {
public static void main(String[] args) {
Computer computer1 = new Computer("2.5 GHz Quad-Core Intel Core i7", "16 GB 1600 MHz DDR3",
new Graphics("Intel Iris Pro","1536 MB"),"820-00xxx");
System.out.println("clone begins ...");
Computer computer2 = (Computer) computer1.clone();
System.out.println("clone ends ...");
System.out.println("computer1.equals(computer2): " + computer1.equals(computer2));
System.out.println("computer1 == computer2: " + (computer1 == computer2));
System.out.println("computer1.getClass() == computer2.getClass(): " + (computer1.getClass() == computer2.getClass()));
System.out.println("set memory of computer 1:");
computer1.setMemory("32G");
System.out.println(computer1);
System.out.println(computer2);
System.out.println("set Graphics of computer 2:");
computer2.getGraphics().setVolume("3072 MB");
System.out.println(computer1);
System.out.println(computer2);
}
}
執行結果
Graphics: construct function called.
Computer: construct function called.
clone begins ...
clone ends ...
computer1.equals(computer2): false
computer1 == computer2: false
computer1.getClass() == computer2.getClass(): true
set memory of computer 1:
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 32G
Graphics: Intel Iris Pro 1536 MB
Motherboard: 820-00xxx
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 16 GB 1600 MHz DDR3
Graphics: Intel Iris Pro 1536 MB
Motherboard: 820-00xxx
set Graphics of computer 2:
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 32G
Graphics: Intel Iris Pro 1536 MB
Motherboard: 820-00xxx
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 16 GB 1600 MHz DDR3
Graphics: Intel Iris Pro 3072 MB
Motherboard: 820-00xxx
可以看到,此種方式之下已經實現了所謂深拷貝,返回的computer2中的Graphics對象並不再是一個指向相同地址的引用,修改不再相互影響。
示例代碼:序列化
除了通過Cloneable接口,通過Serializable接口也可以非常容易地實現深拷貝,將本文的示例代碼簡單修正如下:
import java.io.*;
class Graphics implements Serializable {
private String brand;
private String volume;
public Graphics(String brand, String volume) {
this.brand = brand;
this.volume = volume;
System.out.println("Graphics: construct function called.");
}
public void setBrand(String brand) {
this.brand = brand;
}
public void setVolume(String volume) {
this.volume = volume;
}
public String getBrand() {
return brand;
}
public String getVolume() {
return volume;
}
@Override
public String toString(){
return this.brand + " " + this.volume;
}
}
class Computer implements Serializable {
private String cpu;
private String memory;
private Graphics graphics;
private String motherboard;
public Computer(String cpu, String memory, Graphics graphics, String motherboard) {
this.cpu = cpu;
this.memory = memory;
this.graphics = graphics;
this.motherboard = motherboard;
System.out.println("Computer: construct function called.");
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setMemory(String memory) {
this.memory = memory;
}
public void setGraphics(Graphics graphics) {
this.graphics = graphics;
}
public void setMotherboard(String motherboard) {
this.motherboard = motherboard;
}
public String getCpu() {
return cpu;
}
public String getMemory() {
return memory;
}
public Graphics getGraphics() {
return graphics;
}
public String getMotherboard() {
return motherboard;
}
public Computer copy() throws IOException, ClassNotFoundException {
//get current object
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//create new object
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
return (Computer) ois.readObject();
}
@Override
public String toString(){
return "Computer information: \n"
+ "CPU: " + this.cpu + "\n"
+ "Memory: " + this.memory + "\n"
+ "Graphics: " + this.graphics + "\n"
+ "Motherboard: " + this.motherboard;
}
}
public class TestPrototype {
public static void main(String[] args) {
Computer computer1 = new Computer("2.5 GHz Quad-Core Intel Core i7", "16 GB 1600 MHz DDR3",
new Graphics("Intel Iris Pro","1536 MB"),"820-00xxx");
System.out.println("clone begins ...");
Computer computer2 = null;
try {
computer2 = (Computer) computer1.copy();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("clone ends ...");
System.out.println("computer1.equals(computer2): " + computer1.equals(computer2));
System.out.println("computer1 == computer2: " + (computer1 == computer2));
System.out.println("computer1.getClass() == computer2.getClass(): " + (computer1.getClass() == computer2.getClass()));
System.out.println("set memory of computer 1:");
computer1.setMemory("32G");
System.out.println(computer1);
System.out.println(computer2);
System.out.println("set Graphics of computer 2:");
computer2.getGraphics().setVolume("3072 MB");
System.out.println(computer1);
System.out.println(computer2);
}
}
執行結果
Graphics: construct function called.
Computer: construct function called.
clone begins ...
clone ends ...
computer1.equals(computer2): false
computer1 == computer2: false
computer1.getClass() == computer2.getClass(): true
set memory of computer 1:
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 32G
Graphics: Intel Iris Pro 1536 MB
Motherboard: 820-00xxx
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 16 GB 1600 MHz DDR3
Graphics: Intel Iris Pro 1536 MB
Motherboard: 820-00xxx
set Graphics of computer 2:
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 32G
Graphics: Intel Iris Pro 1536 MB
Motherboard: 820-00xxx
Computer information:
CPU: 2.5 GHz Quad-Core Intel Core i7
Memory: 16 GB 1600 MHz DDR3
Graphics: Intel Iris Pro 3072 MB
Motherboard: 820-00xxx