实践物件的 Deep Copy,避免 Shallow Copy 的问题以及省去重新制作物件。
Prototype 源自於物件的复制问题,如果资料的型别是字串、数字、布林等,可以直接复制(call by value
),物件与阵列这两类只会复制记忆体的储存位置(call by reference
),这将导致复制不完全(Shallow Copy 或 Shallow Clone),新旧物件共用相同的物件与阵列,肯定会出事。
因此,为了完全复制(Deep Copy 或 Deep Clone),可以采用 Prototype,作法是:
Abstract
、Interface
、Class
。Prototype:NewTasteBeverage
public class NewTasteBeverage implements Cloneable {
private String baseBeverage;
private String taste;
private String size;
private IdInfo idInfo;
public NewTasteBeverage(String baseBeverage) {
this.baseBeverage = baseBeverage;
idInfo = new IdInfo();
}
/**
* Deep Clone 需要注意的点
*/
private NewTasteBeverage(IdInfo idInfo) throws CloneNotSupportedException {
this.idInfo = (IdInfo) idInfo.clone();
}
public void setProduceInfo(String location, String factoryAddress) {
idInfo.setLocation(location);
idInfo.setFactoryAddress(factoryAddress);
}
public void setSeriesNumber(String seriesNumber) {
idInfo.setSeriesNumber(seriesNumber);
}
public void setTaste(String taste) {
this.taste = taste;
}
public void setSize(String size) {
this.size = size;
}
@Override
public Object clone() throws CloneNotSupportedException {
NewTasteBeverage clonedObject = new NewTasteBeverage(this.idInfo);
clonedObject.baseBeverage = this.baseBeverage;
clonedObject.taste = this.taste;
clonedObject.size = this.size;
return clonedObject;
}
public void showInfo() {
System.out.println("产品是:" + taste + " " + baseBeverage + " " + size);
System.out.println("产地资讯:" + idInfo.getFactoryAddress() + " " + idInfo.getLocation());
System.out.println("序号:" + idInfo.getSeriesNumber() + "\n");
}
}
class IdInfo implements Cloneable {
private String seriesNumber;
private String location;
private String factoryAddress;
public String getSeriesNumber() {
return seriesNumber;
}
public void setSeriesNumber(String seriesNumber) {
this.seriesNumber = seriesNumber;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getFactoryAddress() {
return factoryAddress;
}
public void setFactoryAddress(String factoryAddress) {
this.factoryAddress = factoryAddress;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
进行复制
public class PrototypeSample {
public static void main(String[] args) throws CloneNotSupportedException {
System.out.println("进行第一项产品的尝试");
NewTasteBeverage beverage1 = new NewTasteBeverage("可乐");
beverage1.setSize("350毫升");
beverage1.setTaste("香草");
beverage1.setProduceInfo("桃园", "台湾");
beverage1.setSeriesNumber("3617263");
System.out.println("更换,进行第二项");
NewTasteBeverage beverage2 = (NewTasteBeverage) beverage1.clone();
beverage2.setTaste("柠檬");
beverage2.setSeriesNumber("8911733");
System.out.println("更换,进行最後一项");
NewTasteBeverage beverage3 = (NewTasteBeverage) beverage1.clone();
beverage3.setProduceInfo("台中", "台湾");
beverage3.setSeriesNumber("1657209");
beverage1.showInfo();
beverage2.showInfo();
beverage3.showInfo();
}
}
Prototype:NewTasteBeverage
class NewTasteBeverage {
constructor(baseBeverage) {
this.baseBeverage = baseBeverage;
this.taste = '';
this.size = '';
this.idInfo = new IdInfo();
}
setProduceInfo(location, factoryAddress) {
this.idInfo.setLocation(location);
this.idInfo.setFactoryAddress(factoryAddress);
}
setSeriesNumber(seriesNumber) {
this.idInfo.setSeriesNumber(seriesNumber);
}
setTaste(taste) {
this.taste = taste;
}
setSize(size) {
this.size = size;
}
clone() {
const clonedObject = Object.create(this);
clonedObject.idInfo = this.idInfo.clone();
clonedObject.baseBeverage = this.baseBeverage;
clonedObject.taste = this.taste;
clonedObject.size = this.size;
return clonedObject;
}
showInfo() {
console.log("产品是:" + this.taste + " " + this.baseBeverage + " " + this.size);
console.log("产地资讯:" + this.idInfo.getFactoryAddress() + " " + this.idInfo.getLocation());
console.log("序号:" + this.idInfo.getSeriesNumber() + "\n");
}
}
class IdInfo {
constructor() {
this.seriesNumber = '';
this.location = '';
this.factoryAddress = '';
}
getSeriesNumber() {
return this.seriesNumber;
}
setSeriesNumber(seriesNumber) {
this.seriesNumber = seriesNumber;
}
getLocation() {
return this.location;
}
setLocation(location) {
this.location = location;
}
getFactoryAddress() {
return this.factoryAddress;
}
setFactoryAddress(factoryAddress) {
this.factoryAddress = factoryAddress;
}
clone() {
const clonedObject = Object.create(this);
clonedObject.seriesNumber = this.seriesNumber;
clonedObject.location = this.location;
clonedObject.factoryAddress = this.factoryAddress;
return clonedObject;
}
}
进行复制
const prototypeSample = () => {
console.log("进行第一项产品的尝试");
const beverage1 = new NewTasteBeverage("可乐");
beverage1.setSize("350毫升");
beverage1.setTaste("香草");
beverage1.setProduceInfo("桃园", "台湾");
beverage1.setSeriesNumber("3617263");
console.log("更换,进行第二项");
const beverage2 = beverage1.clone();
beverage2.setTaste("柠檬");
beverage2.setSeriesNumber("8911733");
console.log("更换,进行最後一项");
const beverage3 = beverage1.clone();
beverage3.setProduceInfo("台中", "台湾");
beverage3.setSeriesNumber("1657209");
beverage1.showInfo();
beverage2.showInfo();
beverage3.showInfo();
}
prototypeSample();
过往的开发 JavaScript
的经验,处理物件的 Deep Clone 时,最常使用的是 JSON.parse(JSON.stringify(obj))
,这个做法的前提物件本身的组成只有 JSON 允许的字串、数字、布林值、物件、阵列,其他像是 undefined、Symbol、Set、Map 会因为 JSON 不支援导致复制後得到的是 {}
。除此之外,物件之间的继承也会变重置,因为 JSON.parse()
会建立新物件,不可能会知道 JSON 前的继承关系。
藉由这个模式,可以了解实作 Deep Clone 是所有物件导向语言的都会遇到的问题,趁着这次的学习与了解,让自己往後遇到拥有复杂继承关系的物件时,多了一种处理方式。
明天将介绍最後一个 Creational patterns: Prototype 模式。
因为考虑到才第三篇就开始飙车直接上 Flask 会不会太快,加上这系列有一小部分原因(大约50%?)...
引言 今天是机派X系列文章的第十二天。 今天会接续昨天的部件介绍,将剩下几个重要的部件介绍给大家。 ...
学完Get请求後就不免要学一下Post请求了,在DAY15: HTTP GET请求的开头有提到,Ge...
前言: 今天我们要来介绍React里很强大的一个工具!没错就是Style Components!废...
来到了最後一天,我们也剩下最後一片云要一起来探索。今天就来谈谈云端的资安,以此来总结我们这一趟经历两...