[设计模式] 原型模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012099869/article/details/78765204

主要内容来自《研磨设计模式》

一、模式定义

用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

原型的本质:克隆生成对象。创建型模式。

二、模式结构

模式结构

三、代码示例

功能描述:当订单数量大于1000时,拆分订单,直至订单数量小于1000。

对外统一的订单接口:

package com.kascend.test.prototype;

/**
 * 订单接口
 *
 * @author wengliemiao
 */
public interface OrderApi {

    /**
     * 获取订单中产品数量
     *
     * @return 订单中产品数量
     */
    int getOrderProNum();

    /**
     * 设置订单中产品数量
     *
     * @param num 订单中产品数量
     */
    void setOrderProNum(int num);

    /**
     * 克隆订单
     *
     * @return 订单原型的实例
     */
    OrderApi cloneOrder();
}

具体的个人订单实现类:

package com.kascend.test.prototype;

/**
 * 个人订单对象
 *
 * @author wengliemiao
 */
public class PersonalOrder implements OrderApi {

    private String customerName;

    private int orderProductNum = 0;

    private Product product = null;

    @Override
    public int getOrderProNum() {
        return this.orderProductNum;
    }

    @Override
    public void setOrderProNum(int num) {
        this.orderProductNum = num;
    }

    /**
     * 获取 customerName
     *
     * @return customerName
     */
    public String getCustomerName() {
        return customerName;
    }

    /**
     * 设置 customerName
     *
     * @param customerName
     */
    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    /**
     * 获取 product
     *
     * @return product
     */
    public Product getProduct() {
        return product;
    }

    /**
     * 设置 product
     *
     * @param product
     */
    public void setProduct(Product product) {
        this.product = product;
    }

    @Override
    public String toString() {
        return "个人订单信息{" +
                "customerName='" + customerName + '\'' +
                ", orderProductNum=" + orderProductNum +
                ", product=" + product +
                '}';
    }

    @Override
    public OrderApi cloneOrder() {
        PersonalOrder personalOrder = new PersonalOrder();
        personalOrder.setCustomerName(this.customerName);
        personalOrder.setOrderProNum(this.orderProductNum);
        personalOrder.setProduct((Product) this.product.cloneProduct()); // 深克隆
        return personalOrder;
    }
}

其中产品信息对象如下(为演示深克隆):

package com.kascend.test.prototype;

/**
 * 克隆产品自身接口
 *
 * @author wengliemiao
 */
public interface ProductPrototype {

    /**
     * 克隆自身产品
     *
     * @return 从自身产品克隆出来的对象
     */
    ProductPrototype cloneProduct();
}
package com.kascend.test.prototype;

/**
 * 产品信息对象
 * 
 * @author wengliemiao
 */
public class Product implements ProductPrototype {

    private String productId;

    private String name;

    public Product() {
    }

    public Product(String productId, String name) {
        this.productId = productId;
        this.name = name;
    }

    /**
     * 获取 productId
     *
     * @return productId
     */
    public String getProductId() {
        return productId;
    }

    /**
     * 设置 productId
     *
     * @param productId
     */
    public void setProductId(String productId) {
        this.productId = productId;
    }

    /**
     * 获取 name
     *
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置 name
     *
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public ProductPrototype cloneProduct() {
        Product product = new Product();
        product.setProductId(this.productId);
        product.setName(this.name);
        return product;
    }

    @Override
    public String toString() {
        return "Product{" +
                "productId='" + productId + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

创建订单类:

package com.kascend.test.prototype;

/**
 * 订单业务处理类
 *
 * @author wengliemiao
 */
public class OrderBusiness {

    /**
     * 创建订单方法
     *
     * @param order 订单信息
     */
    public void saveOrder(OrderApi order) {
        while (order.getOrderProNum() > 1000) {
            // 拆分订单(克隆)
            OrderApi newOrder = order.cloneOrder();
            // 赋值
            newOrder.setOrderProNum(1000);

            // 原来订单数量减去 1000
            order.setOrderProNum(order.getOrderProNum() - 1000);

            System.out.println("拆分订单: " + newOrder);
        }
        System.out.println("订单: " + order);
    }

    public static void main(String[] args) {
        // 个人订单
        PersonalOrder personalOrder = new PersonalOrder();
        personalOrder.setCustomerName("wlmmm");
        personalOrder.setProduct(new Product("1", "产品1"));
        personalOrder.setOrderProNum(6666);

        // 创建订单
        OrderBusiness ob = new OrderBusiness();
        ob.saveOrder(personalOrder);
    }
}

输出为:
原型模式输出

四、Java 克隆

Java 中提供了 clone 方法,需要克隆功能的类,只需实现 java.lang.Cloneable 接口,这个接口没有需要实现的方法,是一个标识接口。

因此可使用 Java 克隆实现原型模式。

修改前面的实现如下:

package com.kascend.test.prototype;

/**
 * 订单接口
 * 
 * @author wengliemiao
 */
public interface OrderApiCloneable {
    /**
     * 获取订单中产品数量
     *
     * @return 订单中产品数量
     */
    int getOrderProNum();

    /**
     * 设置订单中产品数量
     *
     * @param num 订单中产品数量
     */
    void setOrderProNum(int num);
}
package com.kascend.test.prototype;

/**
 * 个人订单对象
 * 
 * @author wengliemiao
 */
public class PersonalOrderCloneable implements Cloneable, OrderApiCloneable {

    private String customerName = "";

    private String productId = "";

    private int orderProNum = 0;

    @Override
    public int getOrderProNum() {
        return this.orderProNum;
    }

    @Override
    public void setOrderProNum(int num) {
        this.orderProNum = num;
    }

    /**
     * 获取 customerName
     *
     * @return customerName
     */
    public String getCustomerName() {
        return customerName;
    }

    /**
     * 设置 customerName
     *
     * @param customerName
     */
    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    /**
     * 获取 productId
     *
     * @return productId
     */
    public String getProductId() {
        return productId;
    }

    /**
     * 设置 productId
     *
     * @param productId
     */
    public void setProductId(String productId) {
        this.productId = productId;
    }

    @Override
    protected Object clone() {
        Object obj = null;
        try {
            obj = super.clone();
        } catch (Exception e) {

        }
        return obj;
    }

    @Override
    public String toString() {
        return "个人订单信息{" +
                "customerName='" + customerName + '\'' +
                ", productId='" + productId + '\'' +
                ", orderProNum=" + orderProNum +
                '}';
    }
}
package com.kascend.test.prototype;

/**
 * Java 克隆实现原型模式测试类
 * 
 * @author wengliemiao
 */
public class OrderCloneTest {

    public static void main(String[] args) {
        PersonalOrderCloneable order = new PersonalOrderCloneable();
        order.setCustomerName("wlmmm");
        order.setProductId("1");
        order.setOrderProNum(100);
        System.out.println("克隆前的实例: " + order);

        PersonalOrderCloneable cloneOrder = (PersonalOrderCloneable) order.clone();
        cloneOrder.setOrderProNum(200);
        System.out.println("克隆的实例: " + cloneOrder);

        System.out.println("克隆后的实例: " + order);
    }
}

输出为:
java 克隆输出

如何实现 Java 深拷贝参看文章: Java 浅拷贝与深拷贝

五、模式优缺点和使用时机

优点:

  1. 对客户端隐藏具体的实现类型。可减少客户端对具体实现类型的依赖
  2. 在运行时动态改变具体的实现类型。由客户来注册符合原型接口的实现类型,也可动态地改变具体的实现类型,但接口不变。

缺点:
每个原型的子类都必须实现 clone 的操作,尤其在包含引用类型的对象时,clone方法会比较麻烦,必须能够递归地让所有的相关对象正确地实现克隆。

使用时机:

  1. 如果一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程。
  2. 如果需要实例化的类时在运行时刻动态指定时,可以使用原型模式,通过克隆原型来得到需要的实例。

猜你喜欢

转载自blog.csdn.net/u012099869/article/details/78765204