设计模式-------原型

   设计模式系列第二篇,原型模式字面意思原始的类型或模型。平时开发参照的开发原型,原型工具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();
		}
	}

}

运行结果:

可以看到,这样是没问题的。这就是深拷贝,深拷贝和浅拷贝区别就在引用对象的拷贝。

浅拷贝相加=深拷贝

当然另一种方式就是实现序列化。

发布了47 篇原创文章 · 获赞 18 · 访问量 5703

猜你喜欢

转载自blog.csdn.net/yuruizai110/article/details/98675117