Go和Java实现原型模式
下面将通过一个克隆的示例来说明原型模式的使用。
1、原型模式
原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对
象的最佳方式之一。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种
模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它
的克隆,在需要的时候更新数据库,以此来减少数据库调用。
-
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
-
主要解决:在运行期建立和删除原型。
-
何时使用:1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定
时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只
能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类
更方便一些。
-
如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。
-
关键代码:1、实现克隆操作,在 JAVA 实现 Cloneable 接口,重写 clone(),在 .NET 中可以使用 Object 类的
MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔
离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。
-
应用实例:1、细胞分裂。 2、JAVA 中的 Object clone() 方法。
-
优点:1、性能提高。 2、逃避构造函数的约束。
-
缺点:1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很
容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable
接口。
-
使用场景:1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、
性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型
模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要
修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出
现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型
模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
-
注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象
的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。
-
适用性:
当一个系统应该独立于它的产品创建、构成和表示时。
当要实例化的类是在运行时刻指定时,例如通过动态装载。
为了避免创建一个与产品类层次平行的工厂层次时。
当一个类的实例只能有几个不同状态组合中的一种时。
建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
2、Go实现原型模式
package prototype// ========== Cloneable ==========
// Cloneable是原型对象需要实现的接口
type Cloneable interface {Clone() Cloneable
}
package prototypeimport "encoding/json"// ========== DeepCopy ==========
// 深拷贝实现Cloneable
type DeepCopy struct {Name string
}// clone也可以使用序列化与反序列化的方式实现深拷贝
func (t *DeepCopy) SerializableClone() Cloneable {var cloneable Cloneableb, _ := json.Marshal(t)json.Unmarshal(b, &cloneable)return cloneable
}func (t *DeepCopy) Clone() Cloneable {tc := *treturn &tc
}
package prototype// ========== ShallowCopy ==========
// 浅拷贝实现Cloneable
type ShallowCopy struct {Name string
}func (t *ShallowCopy) Clone() Cloneable {return t
}
package prototype// ========== PrototypeManager ==========
type PrototypeManager struct {prototypes map[string]Cloneable
}func NewPrototypeManager() *PrototypeManager {return &PrototypeManager{prototypes: make(map[string]Cloneable),}
}func (p *PrototypeManager) Get(name string) Cloneable {return p.prototypes[name].Clone()
}func (p *PrototypeManager) Set(name string, prototype Cloneable) {p.prototypes[name] = prototype
}
package mainimport ("fmt". "proj/prototype"
)var (deepCopyManager *PrototypeManagershallowCopyManager *PrototypeManager
)func init() {deepCopyManager = NewPrototypeManager()deepCopyManager.Set("dc", &DeepCopy{Name: "DeepCopy"})shallowCopyManager = NewPrototypeManager()shallowCopyManager.Set("sc", &ShallowCopy{Name: "ShallowCopy"})
}func main() {// ========== TestDeepCopyClone ==========t1 := deepCopyManager.Get("dc")t2 := t1.Clone()// 深拷贝,指向的不是同一个变量的地址// falsefmt.Println(t1 == t2)t3 := t2.(*DeepCopy)t3.Name = "DeepCopyUpdate"t4 := t1.(*DeepCopy)// 深拷贝Name,不会影响到copy前的变量// DeepCopyUpdatefmt.Println(t3.Name)// DeepCopyfmt.Println(t4.Name)// ========== TestShallowCopyClone ==========t5 := shallowCopyManager.Get("sc")t6 := t5.Clone()// 浅拷贝,变量地址的指向不变// truefmt.Println(t5 == t6)t7 := t6.(*ShallowCopy)t7.Name = "ShallowCopyUpdate"t8 := t5.(*ShallowCopy)// 浅拷贝Name,copy之前的变量和copy之后的变量同时更改// ShallowCopyUpdatefmt.Println(t7.Name)// ShallowCopyUpdatefmt.Println(t8.Name)
}
3、Java实现原型模式
创建一个抽象类 Shape 和扩展了 Shape 类的实体类,然后定义类 ShapeCache,该类把 shape 对象存储在一个
Hashtable 中,并在请求的时候返回它们的克隆。
package com.prototype;// ========== Shape ==========
public abstract class Shape implements Cloneable {private String id;protected String type;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getType() {return type;}public void setType(String type) {this.type = type;}abstract void draw();@Overridepublic Object clone() {Object clone = null;try {clone = super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return clone;}
}
package com.prototype;// ========== Circle ==========
public class Circle extends Shape {public Circle(){type = "Circle";}@Overridepublic void draw() {System.out.println("Draw Circle!");}
}
package com.prototype;// ========== Rectangle ==========
public class Rectangle extends Shape {public Rectangle(){type = "Rectangle";}@Overridepublic void draw() {System.out.println("Draw Rectangle!");}
}
package com.prototype;// ========== Square ==========
public class Square extends Shape {public Square(){type = "Square";}@Overridepublic void draw() {System.out.println("Draw Square!");}
}
package com.prototype;import java.util.Hashtable;// ========== ShapeCache ==========
public class ShapeCache {private final static Hashtable<String, Shape> SHAPE_MAP = new Hashtable<>();public static Shape getShape(String shapeId) {Shape cachedShape = SHAPE_MAP.get(shapeId);// 这里是浅拷贝,返回一个新的对象// 新对象里的引用类型变量地址指向的还是原对象内引用类型地址return (Shape) cachedShape.clone();}// 对每种形状都运行数据库查询,并创建该形状public static void loadCache() {Circle circle = new Circle();circle.setId("1");SHAPE_MAP.put(circle.getId(),circle);Square square = new Square();square.setId("2");SHAPE_MAP.put(square.getId(),square);Rectangle rectangle = new Rectangle();rectangle.setId("3");SHAPE_MAP.put(rectangle.getId(),rectangle);}
}
package com.prototype;public class Test {public static void main(String[] args) {ShapeCache.loadCache();Shape clonedShape = ShapeCache.getShape("1");System.out.println("Shape : " + clonedShape.getType());clonedShape.draw();Shape clonedShape2 = ShapeCache.getShape("2");System.out.println("Shape : " + clonedShape2.getType());clonedShape.draw();Shape clonedShape3 = ShapeCache.getShape("3");System.out.println("Shape : " + clonedShape3.getType());clonedShape3.draw();}
}
# 输出
Shape : Circle
Draw Circle!
Shape : Square
Draw Circle!
Shape : Rectangle
Draw Rectangle!
4、深拷贝和浅拷贝的区别
深拷贝和浅拷贝是指在复制一个对象时,复制的方式不同。
在进行深拷贝时,复制的是对象及其内部的所有对象。这意味着,如果原始对象中包含一个列表,那么在深拷贝
后,原始对象和拷贝对象中的列表是两个独立的对象,即使它们看起来完全相同,也不会相互影响。
相反,浅拷贝仅复制对象本身,但是如果对象内部包含其他对象,则这些对象并不会被复制。因此,如果原始对象
中包含一个列表,那么在浅拷贝后,原始对象和拷贝对象中的列表是同一个对象。如果你在拷贝对象中更改了列
表,那么原始对象中的列表也会发生变化。
总:
复制对象后,如果修改了原对象或新对象的数据,造成了对其他对象的数据也同时发生了变化的现象,就是浅拷
贝,对象之间仍然存在关联。
如果复制后的对象与原对象,无论数据如何变化,都不会对其它对象带来变化,就是深拷贝,对象之间已经毫无关
系。
5、补充Java的深拷贝和浅拷贝的实现
5.1 浅拷贝
package com.shallowcopy;// ========== ShallowCopyDemo ==========
public class ShallowCopyDemo {public static void main(String[] args) {// ======浅拷贝=============System.out.println("======浅拷贝=============");// 初始化对象User user = new User();user.setAge(25);Name name = new Name();name.setFirst("li");name.setSecond("si");user.setName(name);// 实现对象克隆User cloneUser = (User) user.clone();// 修改原始对象属性值cloneUser.setAge(27);cloneUser.getName().setFirst("wang");cloneUser.getName().setSecond("wu");// 源对象:User{age=25, name=Name{first='wang', second='wu'}}System.out.println("源对象:" + user);// 新对象:User{age=27, name=Name{first='wang', second='wu'}}System.out.println("新对象:" + cloneUser);// 在clone()方法中,我们仅对User对象实现了克隆,但是没有对User类下的属性类Name进行克隆// 执行克隆后,新User对象下的Name属性与原对象下的Name属性,仍然指向同一块内存// 如果Name属性发生变更,所有克隆对象的Name属性都会变化,此即为浅克隆}
}class User implements Cloneable{int age;Name name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Name getName() {return name;}public void setName(Name name) {this.name = name;}@Overridepublic String toString() {return "User{" +"age=" + age +", name=" + name +'}';}// 实现Cloneable接口并重写Object类的clone()方法@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}
}class Name{String first;String second;public String getFirst() {return first;}public void setFirst(String first) {this.first = first;}public String getSecond() {return second;}public void setSecond(String second) {this.second = second;}@Overridepublic String toString() {return "Name{" +"first='" + first + '\'' +", second='" + second + '\'' +'}';}
}
5.2 深拷贝
package com.deepcopy;// ========== DeepCopyDemo ==========
public class DeepCopyDemo {public static void main(String[] args) {// ======深拷贝=============System.out.println("======深拷贝=============");// 初始化对象User user = new User();user.setAge(25);Name name = new Name();name.setFirst("li");name.setSecond("si");user.setName(name);// 实现对象克隆User cloneUser = (User) user.clone();// 修改原始对象属性值cloneUser.setAge(27);cloneUser.getName().setFirst("wang");cloneUser.getName().setSecond("wu");// 源对象:User{age=25, name=Name{first='li', second='si'}}System.out.println("源对象:" + user);// 新对象:User{age=27, name=Name{first='wang', second='wu'}}System.out.println("新对象:" + cloneUser);}
}class Name implements Cloneable{String first;String second;public String getFirst() {return first;}public void setFirst(String first) {this.first = first;}public String getSecond() {return second;}public void setSecond(String second) {this.second = second;}@Overridepublic String toString() {return "Name{" +"first='" + first + '\'' +", second='" + second + '\'' +'}';}// 实现Cloneable接口并重写Object类的clone()方法@Overridepublic Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}
}class User implements Cloneable{int age;Name name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Name getName() {return name;}public void setName(Name name) {this.name = name;}@Overridepublic String toString() {return "User{" +"age=" + age +", name=" + name +'}';}// 实现Cloneable接口并重写Object类的clone()方法// 这种方法实现的深克隆比较笨重,如果User类下有多个属性类时,要实现深克隆就需要对所有类重写clone()方法@Overridepublic Object clone() {try {User u = (User) super.clone();//调用属性的克隆方法u.setName((Name) this.name.clone());return u;} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}
}
5.3 fastjson实现深拷贝
package com.fastjsondeepcopy;import com.alibaba.fastjson.JSON;public class FastjsonDemo {public static void main(String[] args) {// ======深拷贝=============System.out.println("======深拷贝=============");// 初始化对象User user = new User();user.setAge(25);Name name = new Name();name.setFirst("li");name.setSecond("si");user.setName(name);// 实现对象克隆String jsonString = JSON.toJSONString(user);User cloneUser = JSON.parseObject(jsonString, User.class);// 修改原始对象属性值cloneUser.setAge(27);cloneUser.getName().setFirst("wang");cloneUser.getName().setSecond("wu");// 源对象:User{age=25, name=Name{first='li', second='si'}}System.out.println("源对象:" + user);// 新对象:User{age=27, name=Name{first='wang', second='wu'}}System.out.println("新对象:" + cloneUser);}
}class User{int age;Name name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Name getName() {return name;}public void setName(Name name) {this.name = name;}@Overridepublic String toString() {return "User{" +"age=" + age +", name=" + name +'}';}
}class Name{String first;String second;public String getFirst() {return first;}public void setFirst(String first) {this.first = first;}public String getSecond() {return second;}public void setSecond(String second) {this.second = second;}@Overridepublic String toString() {return "Name{" +"first='" + first + '\'' +", second='" + second + '\'' +'}';}
}