前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Design Patterns 之原型模式

Design Patterns 之原型模式

作者头像
wsuo
发布2020-12-02 11:54:34
2830
发布2020-12-02 11:54:34
举报
文章被收录于专栏:技术进阶之路技术进阶之路

原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。

原型模式的优点:

  • Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

原型模式的克隆分为浅克隆和深克隆。

  • 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

下面来看一个具体的例子:

创建一个 Horse 类对象,每调用一次 run 方法。该对象的 distance 属性都会递增 10 。

浅克隆

使用 Java 的 Object 的 浅克隆 方式:

Person 类必须先实现 Cloneable 接口。

代码语言:javascript
复制
package com.lsu.prototype.homework;

/**
 * 抽象原型
 *
 * @author wang suo
 * @version 1.0
 * @date 2020/11/30 0030 13:22
 */
public interface Prototype {
    /**
     * 克隆自己
     *
     * @return 返回 obj
     */
    public Object cloneSelf();
}
代码语言:javascript
复制
package com.lsu.prototype.homework;


/**
 * 人
 *
 * @Author wang suo
 * @Date 2020/11/30 0030 14:22
 * @Version 1.0
 */
public class Person implements Cloneable, Prototype {
    /**
     * 坐骑
     */
    private Horse mount;

    Person(Horse horse) {
        super();
        this.mount = horse;
    }

    @Override
    public Object cloneSelf() {
        Object obj = null;
        try {
            obj = clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 人跑路
     */
    void run() {
        // 使用坐骑跑路
        mount.run();
        // 显示路程
        mount.show();
    }
}
代码语言:javascript
复制
package com.lsu.prototype.homework;

import java.io.*;

/**
 * 马
 *
 * @Author wang suo
 * @Date 2020/11/30 0030 13:13
 * @Version 1.0
 */
class Horse implements Serializable {
    private int distance = 0;

    /**
     * 每次距离+10
     */
    void run() {
        this.distance += 10;
    }

    /**
     * 显示路程
     */
    void show() {
        System.out.println("distance = " + this.distance);
    }
}
代码语言:javascript
复制
package com.lsu.prototype.homework;

/**
 * 测试类
 *
 * @Author wang suo
 * @Date 2020/11/30 0030 13:12
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Horse horse = new Horse();
        // 第一个骑士
        Person p1 = new Person(horse);
        // 第二个骑士
        Person p2 = (Person) p1.cloneSelf();
        p1.run();
        p2.run();
    }
}

如上代码,我们克隆了一匹马 h2 我们想要的结果是这两头马各走了 10 米,但是结果却将这两者相加了,明明是两头马跑的距离却被算成了一头马跑的距离。

代码语言:javascript
复制
distance = 10
distance = 20

Process finished with exit code 0

这就是浅克隆,如果调用 clone 方法的当前对象拥有的成员变量是一个对象,那么 clone 方法仅仅是复制了当前对象所拥有的对象的 引用 ,并没有复制这个对象所拥有的变量,所以还是同一个对象。

深度克隆

修改 Person 继承自 Serializable 接口。

代码语言:javascript
复制
package com.lsu.prototype.homework;

/**
 * 抽象原型
 *
 * @author wang suo
 * @version 1.0
 * @date 2020/11/30 0030 13:22
 */
public interface Prototype {
    /**
     * 克隆自己
     *
     * @return 返回 obj
     */
    Object cloneSelf();
}
代码语言:javascript
复制
package com.lsu.prototype.homework;

import java.io.*;

/**
 * 人
 *
 * @Author wang suo
 * @Date 2020/11/30 0030 14:22
 * @Version 1.0
 */
public class Person implements Serializable, Prototype {
    /**
     * 坐骑
     */
    private Horse mount;

    Person(Horse horse) {
        super();
        this.mount = horse;
    }

    @Override
    public Object cloneSelf() {
        Object obj = null;
        try {
            //obj = clone();
            // 使用深克隆
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(this);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            obj = objectInputStream.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 人跑路
     */
    void run() {
        // 使用坐骑跑路
        mount.run();
        // 显示路程
        mount.show();
    }
}
代码语言:javascript
复制
package com.lsu.prototype.homework;

import java.io.*;

/**
 * 马
 *
 * @Author wang suo
 * @Date 2020/11/30 0030 13:13
 * @Version 1.0
 */
class Horse implements Serializable {
    private int distance = 0;

    /**
     * 每次距离+10
     */
    void run() {
        this.distance += 10;
    }

    /**
     * 显示路程
     */
    void show() {
        System.out.println("distance = " + this.distance);
    }
}
代码语言:javascript
复制
package com.lsu.prototype.homework;

/**
 * 测试类
 *
 * @Author wang suo
 * @Date 2020/11/30 0030 13:12
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Horse horse = new Horse();
        // 第一个骑士
        Person p1 = new Person(horse);
        // 第二个骑士
        Person p2 = (Person) p1.cloneSelf();
        p1.run();
        p2.run();
    }
}

测试可以实现深度复制,即包含的对象也不是同一个了。

代码语言:javascript
复制
distance = 10
distance = 10

Process finished with exit code 0

如果我们使用序列化技术将对象保存到本地,然后在想用的时候,再拿出来使用应该如何实现呢?

就像玩游戏的存档一样,玩了几关之后存档,之后再回来继续玩。

修改 Person 的代码如下即可:

代码语言:javascript
复制
package com.lsu.prototype.homework;

import java.io.*;

/**
 * 人
 *
 * @Author wang suo
 * @Date 2020/11/30 0030 14:22
 * @Version 1.0
 */
public class Person implements Serializable, Prototype {
    /**
     * 坐骑
     */
    private Horse mount;

    Person(Horse horse) {
        super();
        this.mount = horse;
    }

    public Horse getMount() {
        return mount;
    }

    @Override
    public Object cloneSelf() {
        Object obj = null;
        try {
            //obj = clone();
            // 使用深克隆
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(this);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            obj = objectInputStream.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 文件存档
     */
    void archive(String path, String fileName) {
        File file = new File(path, fileName);
        boolean b = true;
        ObjectOutputStream objectOutputStream = null;
        try {
            if (!file.exists()) {
                b = file.createNewFile();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (b && file.exists()) {
            // 写入到文件中
            try {
                objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
                objectOutputStream.writeObject(this);
                System.out.println("文件序列化成功,本地地址:"+ path + "文件名:" + fileName);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭输出流
                try {
                    if (objectOutputStream != null) {
                        objectOutputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 读取存档
     */
    Person readArchive(String path, String fileName) {
        File file = new File(path, fileName);
        ObjectInputStream objectInputStream = null;
        Person obj = null;
        // 写入
        try {
            objectInputStream = new ObjectInputStream(new FileInputStream(file));
            obj = (Person) objectInputStream.readObject();
            System.out.println("文件读取成功");
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭输入流
            try {
                if (objectInputStream != null) {
                    objectInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return obj;
    }

    /**
     * 人跑路
     */
    void run() {
        // 使用坐骑跑路
        mount.run();
        // 显示路程
        mount.show();
    }
}
代码语言:javascript
复制
public class Test {
    public static void main(String[] args) {
        Horse horse = new Horse();
        // 第一个骑士
        Person p1 = new Person(horse);
        // 第二个骑士
        Person p2 = (Person) p1.cloneSelf();
        p1.run();
        p2.run();
        // fileName = "archive.txt"; path = "D:"

        /*
        保存到本地
         */
        p2.archive("D:", "archive.txt");
        System.out.println("---------------------------");
        System.out.println("---p2 存档成功! 现在距离--" + p2.getMount().getDistance() + "米");
        System.out.println("---------------------------");

        /*
        实现现场恢复
         */
        Person p3 = p2.readArchive("D:", "archive.txt");
        System.out.println("---------------------------");
        System.out.println("---读取存档成功! 现在距离--" + p3.getMount().getDistance() + "米");
        System.out.println("---------------------------");
        p3.run();
    }
}

这就是原型模式最佳实践。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-11-30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 浅克隆
  • 深度克隆
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档