原型模式
一、定义
原型模式是一种创建型设计模式。能够复制已有对象, 而又无需使代码依赖它们所属的类。
二、问题
有一个订单系统,当订单中的货品数量大于1000时,那么该系统就会分拆成两个订单,如果还大于1000再拆分……
订单:id,货物名称,数量
货物:id,货物名称
三、解决方案
我们就可以使用克隆来完成对订单的复制。
创建一系列不同类型的对象并不同的方式对其进行配置。 如果所需对象与预先配置的对象相同, 那么你只需克隆原型即可, 无需新建一个对象。
支持克隆的对象即为原型(该问题中为订单)
注意:克隆时需要注意深浅拷贝的问题!
浅拷贝:拷贝基本数据类型 如 id,数量
深拷贝:拷贝引用数据类型 如 货物
四、代码实现
1、原型 (Prototype) 接口将对克隆方法进行声明。 在绝大多数情况下, 其中只会有一个名为 clone克隆的方法。
这里使用的是Java自带的克隆接口
package java.lang;
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
2、具体原型 (Concrete Prototype) 类将实现克隆方法。
package com.atmae.prototype;
/**
* @Author: Mae
* @Date: 2022/4/13
* @Time: 11:57
* @Description:
*/
public class Order implements Cloneable {
public Order(Long id, Product product, Integer num) {
this.id = id;
this.product = product;
this.num = num;
}
/**
* 订单id号
*/
private Long id;
/**
* 订单产品
*/
private Product product;
/**
* 产品数量
*/
private Integer num;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
@Override
public String toString() {
return "Order{" +
"id=" + id +
", product=" + product +
", num=" + num +
'}'+'\n';
}
/**
* 深拷贝
* @return 订单对象
* @throws CloneNotSupportedException 克隆?
*/
@Override
protected Object clone() throws CloneNotSupportedException {
Order order = (Order) super.clone();
order.product= (Product) this.product.clone();
return order;
}
}
3、客户端
package com.atmae.prototype;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: Mae
* @Date: 2022/4/13
* @Time: 13:01
* @Description: 客户端
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Product product = new Product("面包", 10001L);
Order order = new Order(10000L, product, 6600);
List<Order> orders = new ArrayList<>();
int num=1;
while (order.getNum() > 1000) {
Order o = (Order) order.clone();
o.setNum(1000);
o.setId(order.getId()+num);
order.setNum(order.getNum() - 1000);
orders.add(o);
num++;
}
orders.add(order);
System.out.println(orders);
}
}
五、UML图
六、原型模式适用场景
- 需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类
七、总结
优点
- 可以克隆对象, 而无需与它们所属的具体类相耦合。
- 可以克隆预生成原型, 避免反复运行初始化代码。
- 可以更方便地生成复杂对象。
- 可以用继承以外的方式来处理复杂对象的不同配置。
缺点
- 如果克隆引用数据类型,需要递归克隆
八、与其他模式的比较
- 与工厂方法模式
原型并不基于继承, 因此没有继承的缺点。 另一方面, 原型需要对被复制对象进行复杂的初始化。
工厂方法基于继承, 但是它不需要初始化步骤。
原型可以起到工厂模式的效果。