• 设计模式之原型模式


    背景:如果有一只狗,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()); } } }
     
  • 相关阅读:
    BZOJ 1221 [HNOI2001] 软件开发 费用流_建模
    BZOJ 1180 / 2843 LCT模板题 + 双倍经验
    bzoj 4372: 烁烁的游戏 动态点分治+树链剖分+线段树
    bzoj 3730: 震波 动态点分治+树链剖分+线段树
    luogu P2634 [国家集训队]聪聪可可 点分治
    bzoj 1468: Tree 点分治
    bzoj 1296: [SCOI2009]粉刷匠 动态规划
    bzoj 1293: [SCOI2009]生日礼物 问题转化 + 性质分析 + 滚动数组优化
    BZOJ 1042: [HAOI2008]硬币购物 容斥原理+背包
    matplotlib模块
  • 原文地址:https://www.cnblogs.com/chenmz1995/p/12422832.html
一二三 - 开发者的网上家园