设计模式系列第二篇,原型模式字面意思原始的类型或模型。平时开发参照的开发原型,原型工具Axure。可以看到原型这个概念还是用途十分普遍的。
java中的原型又是什么呢?
用于创建重复对象,同时又能保持性能。是一种创建对象方式。
何时使用呢?
- 动态装载类。运行时实例化需要的类。
- 一个类的实例只有能有几个不同状态组合中的一种时。一直都要用这个类,使用原型克隆他们比每次实例化对象更方便一些。
使用场景?
- 实际项目中与工厂模式配合使用,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
- 一个对象需要提供给其他对象访问,而且各个对象都可能会修改对象的值。
- 类的创建需要消耗很多资源时。
优势?
- 性能提高。
- 逃避构造函数的约束。
劣势?
- 必须实现Cloneable接口。
- 实现浅拷贝,深拷贝不行。
具体实现:
类图:
创建一个实现Cloneable接口的抽象类
/**
* 抽象类shape
* ClassName: Shape <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON(可选). <br/>
*
* @author yrz
* @version
* @since JDK 1.6
*/
public abstract class Shape implements Cloneable {
private int id;
protected String type;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "shape [id=" + id + ", type=" + type + "]";
}
//复制现有类
@Override
protected Shape clone() {
Object clone = null;
try {
clone = super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return (Shape) clone;
}
}
具体实现类圆
/**
* 具体类圆
* ClassName: Circle <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON(可选). <br/>
* date: 2019年8月6日 下午4:23:25 <br/>
*
* @author yrz
* @version
* @since JDK 1.6
*/
public class Circle extends Shape{
public Circle() {
type = "circle";
}
void draw(){
System.out.println("调用Circle的draw方法");
}
}
具体实现类正方型
/**
* 具体类正方形 ClassName: Square <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON(可选). <br/>
* date: 2019年8月6日 下午4:22:57 <br/>
*
* @author yrz
* @version
* @since JDK 1.6
*/
public class Square extends Shape {
public Square() {
type = "square";
}
void draw() {
System.out.println("调用Square的draw方法");
}
}
创建一个缓存克隆对象的实体类
/**
* 缓存对象类 ClassName: ShapeCache <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON(可选). <br/>
* date: 2019年8月6日 下午4:22:25 <br/>
*
* @author yrz
* @version
* @since JDK 1.6
*/
public class ShapeCache {
private static HashMap<Integer, Shape> shapeMap = new HashMap<Integer, Shape>();
// 获取缓存中的克隆对象
static Shape getShape(int shapeid) {
Shape shape = shapeMap.get(shapeid);
return (Shape) shape.clone();
}
// 在缓存里面加入对象
static void loadCache() {
Circle circle = new Circle();
circle.setId(1);
circle.setType("circle");
shapeMap.put(1, circle);
Square square = new Square();
square.setId(2);
square.setType("square");
shapeMap.put(2, square);
}
}
测试类
/**
* 测试类
* ClassName: prototypeMain <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON(可选). <br/>
* date: 2019年8月6日 下午4:21:27 <br/>
*
* @author yrz
* @version
* @since JDK 1.6
*/
public class prototypeMain {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape(1);
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape(2);
System.out.println("Shape : " + clonedShape2.getType());
}
}
运行结果:
可以看到实现原型设计模式的核心思想还是在创建和保存对象副本。
那LK就必须要和大家一起看看对象的拷贝。
由上面的例子我们克隆的对象都只有基本数据类型,那如果对象中有其他对象的引用呢?来看看这个例子
public class ShallowCopies {
public static void main(String[] args) {
Age age = new Age(20);
Persion persion = new Persion(168, "张三", age);
// 通过调用重写后的clone方法进行浅拷贝
Persion pesion2;
try {
pesion2 = (Persion) persion.clone();
System.out.println(persion.toString());
System.out.println(pesion2.toString());
persion.setName("大傻子");
age.setAge(99);
persion.setHeight(175);
System.out.println(persion.toString());
System.out.println(pesion2.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
static class Persion implements Cloneable {
int height;
String name;
Age age;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
public Persion(int height, String name, Age age) {
super();
this.height = height;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "persion [height=" + height + ", name=" + name + ", age=" + age.getAge() + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = null;
// 调用Object类的clone方法,返回一个Object实例
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
static class Age {
// 年龄类的成员变量(属性)
private int age;
// 构造方法
public Age(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
运行结果:
首先Persion类中持有Age类的对象,Age类没有实现Cloneable接口。其次我们要知道克隆的目的是保持对象的一切都是一样的。我们看到重新对Persion赋值,Persion类持有的Age对象的age属性值发生了改变。原因是引用对象之间的值传递,是地址传递。修改它的属性另一个对象的属性会同步修改。Int类型是基本类型,值传递。String是引用类型,但String类型一旦初始化,常量池中就会存在,所以name值不会改变。
综上就是浅拷贝实现。
如何解决呢?
思考,既然引用对象是地址传递,那我们就让每一个引用对象类都实现Cloneable接口,在顶层类调用它们的克隆方法。不知道行不行,来看看。
public class DeepCopies {
public static void main(String[] args) {
Age age = new Age(20);
Persion persion = new Persion(168, "张三", age);
// 通过调用重写后的clone方法进行浅拷贝
Persion pesion2;
try {
pesion2 = (Persion) persion.clone();
System.out.println(persion.toString());
System.out.println(pesion2.toString());
persion.setName("大傻子");
age.setAge(99);
persion.setHeight(175);
System.out.println(persion.toString());
System.out.println(pesion2.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
// 通过序列化方法实现深拷贝
// AgeNew age = new AgeNew(20);
// PersionNew persion = new PersionNew(168, "张三", age);
// ByteArrayOutputStream bos = new ByteArrayOutputStream();
// try {
// ObjectOutputStream oos = new ObjectOutputStream(bos);
// oos.writeObject(persion);
// oos.flush();
//
// ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
// PersionNew persion1 = (PersionNew) ois.readObject();
// System.out.println(persion.toString());
// System.out.println(persion1.toString());
// System.out.println();
//
// persion.setName("大傻子");
// age.setAge(99);
// persion.setHeight(175);
// System.out.println(persion.toString());
// System.out.println(persion1.toString());
//
// } catch (Exception e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
static class PersionNew implements Serializable {
int height;
String name;
AgeNew age;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AgeNew getAge() {
return age;
}
public void setAge(AgeNew age) {
this.age = age;
}
public PersionNew(int height, String name, AgeNew age) {
super();
this.height = height;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "persion [height=" + height + ", name=" + name + ", age=" + age.getAge() + "]";
}
}
static class AgeNew implements Serializable {
// 年龄类的成员变量(属性)
private int age;
// 构造方法
public AgeNew(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
static class Persion implements Cloneable {
int height;
String name;
Age age;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
public Persion(int height, String name, Age age) {
super();
this.height = height;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "persion [height=" + height + ", name=" + name + ", age=" + age.getAge() + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = null;
// 调用Object类的clone方法,返回一个Object实例
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
Persion persion = (Persion) obj;
// 克隆年龄属性
persion.age = (Age) persion.getAge().clone();
return obj;
}
}
static class Age implements Cloneable {
// 年龄类的成员变量(属性)
private int age;
// 构造方法
public Age(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
}
运行结果:
可以看到,这样是没问题的。这就是深拷贝,深拷贝和浅拷贝区别就在引用对象的拷贝。
浅拷贝相加=深拷贝
当然另一种方式就是实现序列化。