四、原型模式Prototype(创建型)

用原型对象创建对象的种类,并通过拷贝这些原型创建新的对象。在原型设计模式中,这些新建的对象会非常多,它们最大的区别在于初始化时会有所不同,而且还可以被分为若干类。例如在界面设计器中,你可以任意的拖拽出button控件,但是你的界面中可能会需要很多button,你可以选择每次都new一个button给用户,但是更好的办法是clone一个原始button,然后用户自定义这个button属性,这样如果用户复制button时也可以用这个clone方法。


原型模式能够对客户隐藏具体的产品类,从而减少客户需要知道的类数目。原型模式可以和抽象工厂结合,在创建工厂时定制一个原型,然后就可以在任意时刻使用这个定制好后的原型创建新的产品出来。另外,原型模式还会有深拷贝(deep copy)和浅拷贝(shallow copy)之分,深拷贝会复制对象的属性出来,浅拷贝只会复制对象属性的引用。

1.自定义原型

public interface Prototype {
	public String getName();
	public void setName(String name);
	public Prototype myclone();
}
public class ConcretePrototype1 implements Prototype {

	private String name = null;
	public ConcretePrototype1(String name) {
		this.name = name;
	}
	@Override
	public String getName() {
		return this.name;
	}
	@Override
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public Prototype myclone() {
		return new ConcretePrototype1(new String(name));// deep copy
	}
}
public class ConcretePrototype2 implements Prototype {

	private String name = null;
	public ConcretePrototype2(String name) {
		this.name = name;
	}
	@Override
	public String getName() {
		return this.name;
	}
	@Override
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public Prototype myclone() {
		return new ConcretePrototype2(name);// shallow copy
	}
}
public class Client {
	@Test
	public void operation() {
		// deep copy
		Prototype p1 = new ConcretePrototype1("abc");
		Prototype q1 = p1.myclone();
		assertFalse(p1.getName() == q1.getName());
		
		// shallow copy
		Prototype p2 = new ConcretePrototype2("bcd");
		Prototype q2 = p2.myclone();
		assertTrue(p2.getName() == q2.getName());
	}
}

定义Prototype接口,所有需要使用clone方法的对象都实现这个接口,并实现其中的myclone方法。其中ConcretePrototype1实现了深拷贝,也就意味着拷贝时连带对象的属性一起进行了拷贝,而ConcretePrototype2实现了浅拷贝,只是把新对象的属性值指向了原对象相同的那么对象的属性地址。浅拷贝时,如果对象属性是一个可变对象的话,修改原对象和新对象都会影响对方的这个属性的值。

2.Java中支持的原型(使用Object的clone方法,需要继承Cloneable接口)

public class Address implements Cloneable{
	private String city;
	public Address(String city) {
		this.city = city;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	public Object clone(){
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}
}
public class CloneablePrototype1 implements Cloneable {

	private String name = null;
	private Address addr = null;
	public CloneablePrototype1(String name, Address addr) {
		this.name = name;
		this.addr = addr;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Address getAddr() {
		return addr;
	}
	public void setAddr(Address addr) {
		this.addr = addr;
	}
	//shallow copy
	public Object clone() {
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}
}
public class CloneablePrototype2 implements Cloneable {

	private String name = null;
	private Address addr = null;
	public CloneablePrototype2(String name, Address addr) {
		this.name = name;
		this.addr = addr;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Address getAddr() {
		return addr;
	}
	public void setAddr(Address addr) {
		this.addr = addr;
	}
	// deep copy
	public Object clone() {
		try {
			CloneablePrototype2 pro = (CloneablePrototype2) super.clone();
			pro.setName(new String(name));
			pro.setAddr((Address) addr.clone());
			return pro;
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}
}
public class CloneableClient {
	@Test
	public void shallowcopy() {
		Address addr = new Address("Wuhan");
		CloneablePrototype1 p1 = new CloneablePrototype1("abc", addr);
		CloneablePrototype1 q1 = (CloneablePrototype1) p1.clone();
		assertTrue(p1.getAddr() == q1.getAddr());//shallow copy
		assertTrue(p1.getName() == q1.getName());
		
		CloneablePrototype2 p2 = new CloneablePrototype2("bcd", addr);
		CloneablePrototype2 q2 = (CloneablePrototype2) p2.clone();
		assertFalse(p2.getAddr() == q2.getAddr());//deep copy
		assertFalse(p2.getName() == q2.getName());
	}
}

使用Java中的Cloneable来实现clone,在Object对象中包含了protected的clone方法,这样只要实现了Cloneable接口,并覆盖Object的clone方法就可以实现对象的clone。这里也区分深拷贝和浅拷贝,浅拷贝可以直接通过super.clone来实现,但是深拷贝需要创建拷贝对象后再去设定新对象的属性实现深拷贝,这就意味着是否进行深拷贝可以由你自由掌握。

3.使用java中的串行化实现深拷贝

这个方法的确很巧妙,利用Object的IO Stream来读取和解析对象,这样就可以生成和原来对象相同的对象了。但是这个方法要求被克隆的自定义对象的所有域都实现Serializable接口,否则会出现java.io.NotSerializableException错误。

/**
 * reference:
 * <a href='http://www.cnblogs.com/itTeacher/archive/2012/12/02/2797857.html'>
 * Java设计模式四: 原型模式(Prototype Pattern)</a>
 */
public class SerializablePrototype implements Serializable {
	private static final long serialVersionUID = 4653567423161077789L;
	private SerializableName name = null;

	public SerializablePrototype(SerializableName name) {
		this.setName(name);
	}
	public SerializableName getName() {
		return name;
	}
	public void setName(SerializableName name) {
		this.name = name;
	}
	// deep copy
	public Object deepClone() {
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			ObjectOutputStream objOut = new ObjectOutputStream(out);
			objOut.writeObject(this);

			ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
			ObjectInputStream objIn = new ObjectInputStream(in);
			return objIn.readObject();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static class SerializableName implements Serializable {
		private static final long serialVersionUID = 2407657721744992188L;
		private String name;

		public SerializableName(String name) {
			this.name = name;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public String toString() {
			return name;
		}
	}
}
public class SerializableClient {

	@Test
	public void test() {
		SerializableName name = new SerializableName("Wuhan");
		SerializablePrototype p = new SerializablePrototype(name);
		SerializablePrototype q = (SerializablePrototype) p.deepClone();

		System.out.println(q.getName().toString());
		assertFalse(p.getName() == q.getName());
	}
}

这个方法相对有限制,要求属性也必须实现Serializable接口,如果不实现就会出现Exception。实现clone时,将当前对象通过ObjectOutputStream转化为二进制流输入ByteArrayOutputStream,之后将这个output stream交给ByteArrayInputStream,然后就可以通过ObjectInputStream读取这个input stream重构这个对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值