【设计模式In Java】三、原型模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/CL_YD/article/details/87914391

原型模式

定义

原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

场景

在开发一个软件自动化部署工具时,用户可以在WEB页面上创建一个配置文件,然后软件会在用户指定的机器上按照用户的配置创建服务;但改软件的配置项非常多,如果一个个指定会非常麻烦;有时候想在另一个机器上部署一个相同的软件,但仅仅有一个或几个参数配置不同,比如端口,如果再重新创建一个配置文件,无疑工作量是巨大的,这时候我们就会有一个从已有的配置文件拷贝出一个新的配置模板的需求。

因此我们先创建一个适用于大多数场景的通用配置模板存储到数据库,然后用户可以从这个通用模板克隆出一个新的配置,对需要修改的参数做出调整;以后创建配置,可以从通用配置模板拷贝,也可以从其他配置拷贝。

UML图

在这里插入图片描述
示例:

@Test
public void test(){
    MySqlConfig defaultMySqlConfig = loadFromDatabase();
    try {
        MySqlConfig configA = (MySqlConfig) defaultMySqlConfig.cloneConfig();
        configA.addConfigItem("mysqld", "port", "3307");

        MySqlConfig configB = (MySqlConfig) configA.cloneConfig();
        configB.addConfigItem("mysql_safe", "pid-file", "/var/run/mysql/mysql.pid");

        System.out.println("-----------------------default-------------------------");
        System.out.println(defaultMySqlConfig.toString());

        System.out.println("-----------------------configA-------------------------");
        System.out.println(configA.toString());

        System.out.println("-----------------------configB-------------------------");
        System.out.println(configB.toString());
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }

}

/*
-----------------------default-------------------------
[mysql_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
[mysqld]
port=3306
datadir=/var/lib/mysql

-----------------------configA-------------------------
[mysql_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
[mysqld]
port=3307
datadir=/var/lib/mysql

-----------------------configB-------------------------
[mysql_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mysql/mysql.pid
[mysqld]
port=3307
datadir=/var/lib/mysql
*/

代码

prototype

总结

其实可以直接覆盖Object.clone()方法来实现,而不是单独定义一个接口,但单独定义接口可以方便管理。

Java对象拷贝时有深拷贝(deep copy)和浅拷贝(shallow copy)的区别,前者会生成一个与源对象属性完全相同的对象,但所有属性无论是基本数据类型还是引用类型,都会完全复制出新的一份,而不是指向相同的引用;后者也会生成一个与源对象属性完全相同的对象,但引用类型的属性会指向同一个对象。

上面的场景需要使用深克隆,因为原型类里面有属性是引用对象,如果不是深克隆,肯定会导致修改目标对象属性导致源对象发生变化,这是场景中不能出现的情况,而实现深克隆最简单的方式就是先将对象写到流对象流里,再从流里读出该对象(CopyUtil.deepCopy()),注意背写的对象必须实现Serializable接口,否则写入流中将报错。当然可以直接一个一个属性地copy,或使用反射处理,但属性非常多的时候并不是一个好办法。
CopyUtil.deepCopy()代码:

public class CopyUtil {

    public static <T> T deepCopy(T source) throws IOException, ClassNotFoundException {
        Object target;
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(source);
        objectOutputStream.close();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        target = objectInputStream.readObject();
        objectInputStream.close();
        return (T) target;
    }

}

什么时候使用深克隆、什么时候使用浅克隆,什么时候全部克隆、什么时候部分克隆完全取决于业务。在合适的时候可以结合工厂模式、单例模式来统一管理原型:创建一个ConfigManager工厂类,并设计成单例模式,当需要某个原型的时候直接从工厂中获取相应的原型,然后调用克隆方法,UML类图如下,代码参考:prototype
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/CL_YD/article/details/87914391