设计模式讲解与代码实践(四)——原型

本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!
摘要:本文讲解了原型(Prototype)设计模式的使用目的、基本形态及各参与者,并结合示例代码,讲解了该设计模式在具体业务场景下的使用。

1 目的

原型(Prototype)提供了通过拷贝已有原型创建新对象的方法。

2 基本形态

原型的基本形态如类图2-1所示。

设计模式讲解与代码实践(四)——原型_第1张图片
图2-1 原型类图

3 参与者

结合图2-1,下面介绍各类在原型设计模式中扮演的角色。
3.1 Prototype
Prototype是原型父类,提供了拷贝自身的接口。Prototype既可以是接口也可以是包含默认实现的普通类。从封装的角度来讲,一般来说Prototype应该是包含默认实现的普通类,以负责拷贝原型时为各具体原型的共有成员赋值。
Prototype在java中一般需要实现Cloneable接口的clone方法。需要重点注意的是拷贝生成对象时的深拷贝与浅拷贝的问题,这需要根据具体业务场景加以区分。
3.2 ConcretePrototype
ConcretePrototyp1和ConcretePrototyp2是具体原型,它们都派生自Prototype。它们通过重写clone方法实现对具体原型的拷贝。
在clone方法的实现上,ConcretePrototyp往往通过调用父类Prototype的clone方法来填充从父类继承的属性,再处理自有的属性的拷贝。
3.3 Client
Client是Prototype设计模式的使用者,通过调用Prototype的clone方法来创建新的对象。在典型的应用场景中,Client并不区分Prototype对象所属的具体类,而是用Prototype类型对象接收拷贝的结果。

4 代码实践

下面我们用一个业务场景实例来进一步讲解原型的使用。
4.1 场景介绍
某电商平台使用采购单和销售单两种主要单据,除了单据id、单据创建时间、单据创建人等共有信息外,每种单据还包含个性化信息。平台拥有统一的单据管理功能。利用该功能使用者可以查看自己创建的各单据的明细,并可以以已有单据为基础(原型)创建新的单据。
以下各节将介绍该场景各类的具体实现及其在原型设计模式中所对应的参与者角色。
4.2 Order
Order是单据类,声明了单据id、单据创建时间、单据创建人、单据类型等单据基本属性,并提供了单据信息的序列化方法。Order实现接口Cloneable,重写了clone方法,返回拷贝自身信息生成的新的Order对象。
对应于原型模式的参与者,Order是我们的Prototype。下面的代码给出了Order的声明。

package demo.designpattern.prototype;
import java.util.Date;
import java.util.UUID;

/**
 * 单据类
 * Created by LiMingzi on 2017/6/1.
 */
public class Order implements Cloneable{
    /**
     * 单据id
     */
    private String id;
    /**
     * 单据创建时间
     */
    private Date createTime;
    /**
     * 单据创建人
     */
    private String creator;
    /**
     * 单据类型
     */
    private String type;

    /**
     * 构造方法
     * @param id 单据id
     * @param createTime 单据创建时间
     * @param creator 单据创建者
     * @param type 单据类型
     */
    public Order(String id, Date createTime, String creator,String type) {
        this.id = id;
        this.createTime = createTime;
        this.creator = creator;
        this.type=type;
    }

    /**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object.
     *
     * @return a clone of this instance.
     * @throws CloneNotSupportedException if the object's class does not
     *                                    support the Cloneable interface. Subclasses
     *                                    that override the clone method can also
     *                                    throw this exception to indicate that an instance cannot
     *                                    be cloned.
     * @see Cloneable
     */
    @Override
    public Order clone() throws CloneNotSupportedException {
        return new Order(UUID.randomUUID().toString(),new Date(),this.creator,this.type);
    }

    /**
     * 序列化
     * @return a string representation of the object.
     */
    @Override
    public String toString() {
        return "单据类型:"+this.getType()+"\n单号:"+this.getId()+"\n创建人:"+this.getCreator()+"\n创建时间:"+this.getCreateTime().toString()+"\n";
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public String getCreator() {
        return creator;
    }

    public void setCreator(String creator) {
        this.creator = creator;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

上面代码第55行,通过调用构造方法创建了新的Order对象。这里,单据创建人和单据类型拷贝了单据对象本身,而单据id为随机生成的UUID,单据创建时间为当前时间。
4.3 PurchaseOrder
PurchaseOrder是采购单类,派生自Order类。除了继承自Order的单据基本属性外,还包含商品名、采购数量等信息。PurchaseOrder重写了序列化方法toString及拷贝方法clone。对应于原型模式的参与者,PurchaseOrder是我们的具体原型ConcretePrototype。下面的代码给出了PurchaseOrder的声明。

package demo.designpattern.prototype;

import java.util.Date;

/**
 * 采购单
 * Created by LiMingzi on 2017/6/1.
 */
public class PurchaseOrder extends Order {
    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    /**
     * 商品名
     */
    private String productName;
    /**
     * 采购数量
     */
    private int count;
    /**
     * 构造方法
     *
     * @param id         单据id
     * @param createTime 单据创建时间
     * @param creator    单据创建者
     * @param productName 产品名称
     * @param count 产品数量
     */
    public PurchaseOrder(String id, Date createTime, String creator,String productName,int count) {
        super(id, createTime, creator, "采购单");
        this.productName=productName;
        this.count=count;
    }

    /**
     * 构造方法
     *
     * @param order         单据对象
     * @param productName 产品名称
     * @param count 产品数量
     */
    public PurchaseOrder(Order order,String productName,int count) {
        super(order.getId(), order.getCreateTime(), order.getCreator(), "采购单");
        this.productName=productName;
        this.count=count;
    }

    /**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object.
     *
     * @return a clone of this instance.
     * @throws CloneNotSupportedException if the object's class does not
     *                                    support the Cloneable interface. Subclasses
     *                                    that override the clone method can also
     *                                    throw this exception to indicate that an instance cannot
     *                                    be cloned.
     * @see Cloneable
     */
    @Override
    public PurchaseOrder clone() throws CloneNotSupportedException {
        // 新单据
        PurchaseOrder newOrder = new PurchaseOrder(super.clone(),this.productName,this.count);
        return newOrder;
    }

    /**
     * 序列化
     * @return a string representation of the object.
     */
    @Override
    public String toString() {
        return super.toString()+"产品名称:"+this.productName+"\n数量:"+this.count+"\n";
    }
}

上面代码第77行,clone方法通过调用父类的clone方法生成新的Order对象,再以Order对象为参数调用PurchaseOrder的构造方法生成新的PurchaseOrder对象。
4.4 SalesOrder
SalesOrder是销售单类,派生自Order类。除了继承自Order的单据基本属性外,还包含商品名、销售数量、单价等信息。SalesOrder重写了序列化方法toString及拷贝方法clone。对应于原型模式的参与者,SalesOrder是我们的具体原型ConcretePrototype。下面的代码给出了SalesOrder的声明。

package demo.designpattern.prototype;

import java.util.Date;

/**
 * 销售单
 * Created by LiMingzi on 2017/6/1.
 */
public class SalesOrder extends Order{
    /**
     * 产品名
     */
    private String productName;
    /**
     * 数量
     */
    private int count;
    /**
     * 单价,为0时表示未设置,单据信息不完整
     */
    private double unitPrice;

    /**
     * 构造方法
     * @param id         单据id
     * @param createTime 单据创建时间
     * @param creator    单据创建者
     * @param productName 产品名称
     * @param count 产品数量
     * @param unitPrice 产品单价
     */
    public SalesOrder(String id, Date createTime, String creator, String productName, int count, double unitPrice) {
        super(id, createTime, creator, "销售单");
        this.productName = productName;
        this.count = count;
        this.unitPrice = unitPrice;
    }
    /**
     * 构造方法
     * @param order         单据对象
     * @param productName 产品名称
     * @param count 产品数量
     * @param unitPrice 产品单价
     */
    public SalesOrder(Order order, String productName, int count, double unitPrice) {
        super(order.getId(),order.getCreateTime(),order.getCreator(), "销售单");
        this.productName = productName;
        this.count = count;
        this.unitPrice = unitPrice;
    }
    /**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object.
     *
     * @return a clone of this instance.
     * @throws CloneNotSupportedException if the object's class does not
     *                                    support the Cloneable interface. Subclasses
     *                                    that override the clone method can also
     *                                    throw this exception to indicate that an instance cannot
     *                                    be cloned.
     * @see Cloneable
     */
    @Override
    public SalesOrder clone() throws CloneNotSupportedException {
        // 新单据
        SalesOrder newOrder = new SalesOrder(super.clone(),this.productName,this.count,0.0f);
        return newOrder;
    }

    /**
     * 序列化
     * @return a string representation of the object.
     */
    @Override
    public String toString() {
        return super.toString()+"产品名称:"+this.productName+"\n数量:"+this.count+"\n单价:"+unitPrice+"\n";
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public double getUnitPrice() {
        return unitPrice;
    }

    public void setUnitPrice(double unitPrice) {
        this.unitPrice = unitPrice;
    }
}

上面代码第66行,clone方法通过调用父类的clone方法生成新的Order对象,再以Order对象为参数调用SalesOrder的构造方法生成新的SalesOrder对象。这里,因为商品的单价是经常变化的,因此在拷贝销售单时并不拷贝产品单价。
4.5 OrderMgmt
OrderMgmt是单据管理类,实现了单据的批量参考新增功能。对应于原型模式的参与者,OrderMgmt是Client。下面的代码给出了OrderMgmt的声明。

package demo.designpattern.prototype;

import java.util.ArrayList;
import java.util.List;

/**
 * 单据管理类
 * Created by LiMingzi on 2017/6/2.
 */
public class OrderMgmt {
    /**
     * 参考新增
     * @param srcOrders 原始单据集合
     */
    public void refAdd(ListsrcOrders) throws CloneNotSupportedException {
        System.out.println("原始单据:");
        for (Order srcOrder : srcOrders) {
            System.out.print(srcOrder.toString());
            System.out.println("------------------------------------------------------------------------");
        }
        // 目标单据
        List desOrders = new ArrayList();
        for (Order srcOrder : srcOrders) {
            // 目标单据
            Order desOrder = srcOrder.clone();
            if(desOrder instanceof SalesOrder){
                ((SalesOrder) desOrder).setUnitPrice(getUnitPrice(((SalesOrder) desOrder).getProductName()));
            }
            desOrders.add(desOrder);
        }
        System.out.println("新增单据:");
        for (Order desOrder : desOrders) {
            System.out.print(desOrder.toString());
            System.out.println("------------------------------------------------------------------------");
        }
    }

    /**
     * 获取产品单价(demo)
     * @param productName 产品名
     * @return 单价
     */
    private double getUnitPrice(String productName){
        // 单价
        double unitPrice = 0.0f;
        if("婴儿车".equals(productName)){
            unitPrice = 999.0f;
        }
        return unitPrice;
    }
}

上面的代码中,第15行声明了单据的批量参考新增方法。第23行,通过遍历原始单据,并调用各单据的clone方法产生新的单据类Order对象。注意,第26行通过对单据类型的判断,为销售单中的商品设置了新的单价。这种形式在实际的业务场景中经常出现,即通过调用原型的clone方法生成的对象并不一定包含完整的信息,部分信息需要在Client中进一步完善。
第43行根据产品名获取产品单价的方法只是一个样例,真实场景下单价需要调用相应的模块方法或查询数据库获取。
4.6 测试代码
为了测试本文中的代码,我们可以编写如下测试代码。

    /**
     * 原型测试
     */
    public static void prototypeTest() throws CloneNotSupportedException {
        // 原始单据集合
        List srcOrders = new ArrayList();
        // 原始采购单
        PurchaseOrder srcPurchaseOrder = new PurchaseOrder("c0f55f62-4979-4e87-8cd9-1c556894e2bb",new Date(new GregorianCalendar(2017,0,25).getTimeInMillis()),"张三","婴儿口水巾",400);
        srcOrders.add(srcPurchaseOrder);
        // 原始销售单
        SalesOrder srcSalesOrder = new SalesOrder("12fb4958-bee2-4c89-8cf8-edea1177b21f",new Date(new GregorianCalendar(2017,0,13).getTimeInMillis()),"张三","婴儿车",1,1280.0f);
        srcOrders.add(srcSalesOrder);
        // 单据管理类对象
        OrderMgmt orderMgmt = new OrderMgmt();
        orderMgmt.refAdd(srcOrders);
    }

编译运行后,得到如下测试结果:

原始单据:
单据类型:采购单
单号:c0f55f62-4979-4e87-8cd9-1c556894e2bb
创建人:张三
创建时间:Wed Jan 25 00:00:00 CST 2017
产品名称:婴儿口水巾
数量:400


单据类型:销售单
单号:12fb4958-bee2-4c89-8cf8-edea1177b21f
创建人:张三
创建时间:Fri Jan 13 00:00:00 CST 2017
产品名称:婴儿车
数量:1
单价:1280.0


新增单据:
单据类型:采购单
单号:f34c722c-d613-4ff3-afdf-2aac252e29d5
创建人:张三
创建时间:Fri Jun 02 13:59:37 CST 2017
产品名称:婴儿口水巾
数量:400


单据类型:销售单
单号:f9b4dc6b-e8f6-4432-b9f2-1bce6174f501
创建人:张三
创建时间:Fri Jun 02 13:59:37 CST 2017
产品名称:婴儿车
数量:1
单价:999.0


你可能感兴趣的:(算法与程序设计,设计模式,java,架构设计,设计模式讲解与代码实践)