设计模式之原型模式

Posted chenmz1995

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之原型模式相关的知识,希望对你有一定的参考价值。

 

背景:如果有一只狗,Jim, 现在需要创建5只,跟Jim一样的狗(属性一样的),按常规的做法如下:

 

Dog:

public class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void show() {
        System.out.println("名字:" + name + ",年龄:" + age);
    }
}

 

Client:

package prototypePattern;

public class Client {
    public static void main(String[] args) {
        //先把Jim创建出来
        Dog jim = new Dog("Jim", 2);
        //然后创建5只跟Jim一样的狗
        Dog dog = new Dog(jim.getName(), jim.getAge());
        Dog dog1 = new Dog(jim.getName(), jim.getAge());
        Dog dog2 = new Dog(jim.getName(), jim.getAge());
        Dog dog3 = new Dog(jim.getName(), jim.getAge());
        Dog dog4 = new Dog(jim.getName(), jim.getAge());
    }
}

 

分析:

优点:相当的简明,逻辑异常清晰。容易实现。

缺点:总是要去一个个获取Jim的属性,问题是,这仅仅只是个简单例子,万一有一个超级无比大的对象,如果也是这样一个个获取下去,势必会造成效率低下问题,写起来超级累,如果属性个数被拿去拓展,要改超级多的地方。

 

改进方式:采用原型模式

 

代码实现如下:

 技术图片

 

1、Prototype是一个原型类,声明一个克隆自己的接口。在这边相当于Clonable接口。

2、concretePrototype是一个具体的原型类,实现一个克隆自己的操作。

 

Client:

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        //先把Jim创建出来
        Dog jim = new Dog("Jim", 2);
        //然后创建5只跟Jim一样的狗
        Dog dog1 = (Dog) jim.clone();
        Dog dog2 = (Dog) jim.clone();
        Dog dog3 = (Dog) jim.clone();
        Dog dog4 = (Dog) jim.clone();
        Dog dog5 = (Dog) jim.clone();
    }
}

 

Dog:

public class Dog implements Cloneable{
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Dog dog = (Dog) super.clone();
        return dog;
    }

    @Override
    public String toString() {
        return "名字:" + name + ",年龄:" + age;
    }
}

 

优点:让程序有更高的效率和拓展性。这个是很明显,如果你要添加一个属性,用通俗的方式,那每个对象还得都加一遍。

 创建出来的对象虽然属性都是一样的,但是是属于不同的对象实例。

 

深拷贝&浅拷贝

现在问题是,如果每个dog中还有一个引用类型的变量,如数组或者类的对象。

 测试如下,在Dog类中添加一个引用的变量。看下结果。

Dog.java(添加一个引用变量)

package prototypePattern;

public class Dog implements Cloneable{
    private String name;
    private int age;
    private Dog friend;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Dog getFriend() {
        return friend;
    }

    public void setFriend(Dog friend) {
        this.friend = friend;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Dog dog = (Dog) super.clone();
        return dog;
    }

    @Override
    public String toString() {
        return "名字:" + name + ",年龄:" + age;
    }
}

 

Client.java

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        //先把Jim创建出来
        Dog jim = new Dog("Jim", 2);
        jim.setFriend(new Dog("Tom", 2));
        Dog dog1 = (Dog) jim.clone();
        System.out.println("jim:"+jim.hashCode()+"   dog1:"+dog1.hashCode());
        System.out.println("jim.friend:"+jim.getFriend().hashCode()+"   dog1.friend:"+dog1.getFriend().hashCode());
    }
}

 

输出结果:

jim:342597804   dog1:1308244637
jim.friend:1860944798   dog1.friend:1860944798

可以看到:虽然jim和dog1是不同的实例,但是里面的引用变量(即friend) 却是同一个实例。所以clone方法实现的属于浅拷贝。

 

那么如何实现深拷贝?有以下两种方式。

一、重写clone方法的时候对引用类型的变量进行特殊处理

DeepCloneableTarget.java

import java.io.Serializable;

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.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
import java.io.Serializable;

public class DeepProtoType implements Serializable, Cloneable {
    public String name;
    public DeepCloneableTarget deepCloneableTarget;

    public DeepProtoType(String name, DeepCloneableTarget deepCloneableTarget) {
        this.name = name;
        this.deepCloneableTarget = deepCloneableTarget;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        DeepProtoType deepProtoType = (DeepProtoType) super.clone();
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepProtoType.deepCloneableTarget.clone();
        return deepProtoType;
    }
}

Client.java:

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        DeepProtoType deepProtoType = new DeepProtoType("deepProtoType", new DeepCloneableTarget("DeepCloneableTarget-name", "DeepCloneableTarget-class"));
        DeepProtoType clone = (DeepProtoType) deepProtoType.clone();
        System.out.println("deepProtoType:"+deepProtoType.hashCode()+"   clone:"+clone.hashCode());
        System.out.println("deepProtoType.deepCloneableTarget:"+deepProtoType.deepCloneableTarget.hashCode()
                +"   clone.deepCloneableTarget:"+clone.deepCloneableTarget.hashCode());
    }
}

输出结果:

deepProtoType:1308244637   clone:1860944798
deepProtoType.deepCloneableTarget:1179381257   clone.deepCloneableTarget:258754732

 

以上案例:DeepProtoType类中有一个DeepCloneableTarget类型的成员变量,通过clone()中特殊处理后,发现确实实现了深拷贝。但是如果DeepCloneableTarget中还有引用变量的,那么就难办了。还得不断的特殊处理下去。

 

二、通过序列化、反序列化 (推荐)

    @Override
    protected Object clone() throws CloneNotSupportedException {
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
//序列化 bos
= new ByteArrayOutputStream(); 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 e) { e.printStackTrace(); return null; } finally { try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (IOException e) { System.out.println(e.getMessage()); } } }

 

 

以上是关于设计模式之原型模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之原型模式

设计模式之原型

设计模式之原型模式

PHP设计模式之工厂模式和原型模式

一天学习一个设计模式之原型模式

go设计模式之原型模式