Java设计模式之原型模式

原型模式(Prototype Pattern)

原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。
工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动船舰的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()方法。
类似于:孙悟空吹猴毛,变出其他孙悟空。

原型模式UML类图

类图

  1. Prototype: 原型类,声明一个克隆自己的接口
  2. ConcretePrototype: 具体的原型类,实现一个克隆自己的操作。
  3. Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样)

引入一个需求

创建一个Sheep对象,名称为:小羊,年龄为:2,颜色为:白色。创建出和小羊属性相同的5只羊。

传统方式

代码演示:

public class Sheep {

    private String name;
    private Integer age;
    private String color;

    public Sheep(String name,Integer age,String color){
        this.name = name;
        this.age = age;
        this.color = color;
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public String getColor() { return color; }
    public void setColor(String color) { this.color = color; }
}
//创建
    public static void main(String[] args) {
        Sheep sheep = new Sheep("小羊",2,"白色");

        Sheep sheep1 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        Sheep sheep2 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        Sheep sheep4 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        Sheep sheep5 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
    }

传统方式优缺点说明:

  1. 优点是:比较好理解,简单易操作。
  2. 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低。
  3. 总是需要重新初始化对象,而不是动态的获得对象运行时的状态,不够灵活。

原型模式方式

实例代码:

public class Sheep implements Cloneable{

    private String name;
    private Integer age;
    private String color;
    public Sheep friend; //增加一个对象属性

    public Sheep(String name,Integer age,String color){
        this.name = name;
        this.age = age;
        this.color = color;
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public String getColor() { return color; }
    public void setColor(String color) { this.color = color; }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "name="+name + "age=" +age + "color=" +color;
    }
}
//调用
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep = new Sheep("小羊",2,"白色");
        sheep.friend = new Sheep("小羊对象属性",2,"黑色");
        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        Sheep sheep4 = (Sheep) sheep.clone();
        Sheep sheep5 = (Sheep) sheep.clone();

        System.out.println(sheep1 + "  firend=" + sheep1.friend);
        System.out.println(sheep2 + "  firend=" + sheep1.friend);
        System.out.println(sheep3 + "  firend=" + sheep1.friend);
        System.out.println(sheep4 + "  firend=" + sheep1.friend);
        System.out.println(sheep5 + "  firend=" + sheep1.friend);
    }

运行结果如下:
Clone对象实现
原型模式优缺点说明:

  1. 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
  2. 不用重新初始化对象,而是动态的获取对象运行时的状态。
  3. 如果原始对象发生变化(增加或者减少属性),其他克隆对象的也会发生相应的变化,无需修改代码
  4. 在实现深克隆的时候可能需要比较复杂的代码
  5. 需要为每一个类配备一个克隆方法配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,需要修改其源码,违背了OCP原则,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

深入讨论-浅拷贝和深拷贝

浅拷贝介绍

  1. 对于数据类型是基本类型的成员变量,浅拷贝回直接进行值传递,也就是将该属性值复制一份给新的对象。
  2. 对于数据类型是引用类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝回进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
  3. 浅拷贝是使用默认的clone()方法来实现。

查看上述需求对象各个属性地址
运行代码:

    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep = new Sheep("小羊",2,"白色");
        sheep.friend = new Sheep("小羊对象属性",2,"黑色");
        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        Sheep sheep4 = (Sheep) sheep.clone();
        Sheep sheep5 = (Sheep) sheep.clone();
        System.out.println(sheep +" " + sheep.getName().hashCode() +"  firend="+ sheep.friend.hashCode());
        System.out.println(sheep1 +" "+ sheep1.getName().hashCode() + "  firend=" + sheep1.friend.hashCode());
        System.out.println(sheep2 +" "+ sheep2.getName().hashCode() + "  firend=" + sheep2.friend.hashCode());
        System.out.println(sheep3 +" "+ sheep3.getName().hashCode() + "  firend=" + sheep3.friend.hashCode());
        System.out.println(sheep4 +" "+ sheep4.getName().hashCode() + "  firend=" + sheep4.friend.hashCode());
        System.out.println(sheep5 +" "+ sheep5.getName().hashCode() + "  firend=" + sheep5.friend.hashCode());
    }

运行结果
示例

深拷贝介绍

  1. 复制对象的所有基本数据类型的成员变量值。
  2. 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝。

实现方式有两种:

  1. 重写clone方法来实现
  2. 通过对象序列化来实现深拷贝(推荐使用)

方式一代码实现:

public class DeepCloneableTarget implements Serializable,Cloneable {
    private static final long serialVersionUID = 1L;

    private String cloneName;
    private String cloneClass;

    public DeepCloneableTarget(String cloneName,String cloneClass){
        this.cloneClass = cloneClass;
        this.cloneName = cloneName;
    }
    //因为该类的属性都是基本类型,使用默认clone 即可
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class DeepProtoType implements Serializable,Cloneable {
    private static final long serialVersionUID = 1L;
    public String name;
    public DeepCloneableTarget deepCloneableTarget;


    @Override
    protected Object clone() throws CloneNotSupportedException {

        Object deep = null;
        deep = super.clone();
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
        return deepProtoType;
    }
}
//调用
    public static void main(String[] args) throws CloneNotSupportedException {

        DeepProtoType p = new DeepProtoType();
        p.name = "张三";
        p.deepCloneableTarget = new DeepCloneableTarget("大三","小三");

        DeepProtoType p1 = (DeepProtoType) p.clone();

        System.out.println("p.name=" + p.name.hashCode() + "p.deepCloneableTarget="+p.deepCloneableTarget.hashCode());
        System.out.println("p1.name=" + p1.name.hashCode() + "p1.deepCloneableTarget="+p1.deepCloneableTarget.hashCode());
    }

运行结果:
示例
方式二代码实现:

    public Object deepClone(){
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try{
            //序列化
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType o = (DeepProtoType) ois.readObject();
            return o;
        }catch (Exception ex){
            ex.printStackTrace();
            return null;
        }finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //调用
    public static void main(String[] args) throws CloneNotSupportedException {

        DeepProtoType p = new DeepProtoType();
        p.name = "张三";
        p.deepCloneableTarget = new DeepCloneableTarget("大三","小三");
        DeepProtoType p1 = (DeepProtoType) p.deepClone(); //调用方法
        System.out.println("p.name=" + p.name.hashCode() + "  p.deepCloneableTarget="+p.deepCloneableTarget.hashCode());
        System.out.println("p1.name=" + p1.name.hashCode() + " p1.deepCloneableTarget="+p1.deepCloneableTarget.hashCode());
    }

运行结果:
示例2
感谢阅读

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值