设计模式之原型模式

作者: 无聊到学习 | 来源:发表于2020-10-05 23:47 被阅读0次

    一、简介

    原型模式是一种创建型设计模式,允许使用已有的实例对象作为原型,创建新的对象,无需知道任何如何创建的细节,也就是“克隆指定对象”。

    实现原型模式的思想是让需要拷贝的原型类必须实现"java.lang.Cloneable"接口,然后重写Object类中的clone方法。

    Cloneable是一个“标记接口”,所谓的标记接口就是该接口中没有任何内容,但只有实现了Cloneable接口的类才有资格调用Object类中的clone方法(该方法提供了浅拷贝的功能),否则会抛出“CloneNotSupportedException”异常。

    原型模式主要有两类角色:
    1.抽象原型角色((Prototype):
    实现Cloneable接口重写clone方法(可定义为抽象,也可具体实现,视情况而定),来说明它有被克隆功能。
    2.具体原型(Concrete Prototype)角色
    继承抽象原型角色,则具体原型角色自动具有拷贝功能,这样节省代码量,当然也可以让具体原型角色直接实现实现Cloneable接口。

    原型模式的类图如下:


    原型模式.png

    二、使用场景

    当对象的创建非常复杂,可以使用原型模式快捷的创建对象。或者在运行过程中不知道对象的具体类型,可使用原型模式创建一个相同类型的对象。

    使用原型模式的优点在于可以基于原型,快速的创建一个对象,而无需知道创建的细节。另外,由于clone方法是由虚拟机直接复制内存块执行,不会执行构造方法,避免了初始化需要的时间

    三、举例

    public class 原型模式{
        public static void main(String[] args) {
            Car1ConcretePrototype car1_1=new Car1ConcretePrototype("car1",100,new ArrayList<String>(){{add("hello1");}});
            Car1ConcretePrototype car1_2=(Car1ConcretePrototype)car1_1.clone();
            Car2ConcretePrototype car2_1=new Car2ConcretePrototype("car2",200,new ArrayList<String>(){{add("hello2");}});
            Car2ConcretePrototype car2_2=(Car2ConcretePrototype)car2_1.clone2();
            car1_1.show();car1_2.show();
            car2_1.show();car2_2.show();
        }
    }
    class ICarPrototype implements Cloneable,Serializable{
        private static final long serialVersionUID = 1L;
        private String name;
        private int price;
        private ArrayList<String>otherInfo;
        public ICarPrototype(String name, int price, ArrayList<String> otherInfo) {
            this.name = name;
            this.price = price;
            this.otherInfo = otherInfo;
        }
        public String getName() {
            return name;
        }
        public int getPrice() {
            return price;
        }
        public ArrayList<String> getOtherInfo() {
            return otherInfo;
        }
        //深度克隆方式1
        @Override
        protected Object clone() {
            ICarPrototype cloneResult=null;
            try {
                //可以对基本数据类型进行复制,即Object中的clone方法实现的只是浅拷贝
                cloneResult=(ICarPrototype)super.clone();
                //下面对每一个复杂成员对象(如数组、容器、引用对象等)分别进行拷贝,以实现对象的深拷贝
                cloneResult.otherInfo=(ArrayList<String>)this.otherInfo.clone();
            } catch (CloneNotSupportedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            return cloneResult;
        }
        //深度克隆方式2,通过序列化对象实现深拷贝,需要实现Serializable接口
        protected Object clone2() {
            //声明流对象
            ByteArrayOutputStream bos=null;
            ByteArrayInputStream bis=null;
            ObjectOutputStream oos=null;
            ObjectInputStream ois=null;
            try{
                //创建序列化流
                bos=new ByteArrayOutputStream();
                oos=new ObjectOutputStream(bos);
                //将当前对象以对象流的形式输出
                oos.writeObject(this);
                //创建反序列化流
                bis=new ByteArrayInputStream(bos.toByteArray());
                ois=new ObjectInputStream(bis);
                //将流对象反序列化,从而实现对象的深度拷贝
                return ois.readObject();
            }catch(Exception e){
                e.printStackTrace();
                return null;
            }finally{
                //释放资源
                try {
                    bos.close();bis.close();
                    oos.close();ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    class Car1ConcretePrototype extends ICarPrototype{
        private static final long serialVersionUID = 2L;
        public Car1ConcretePrototype(String name, int price,ArrayList<String> otherInfo) {
            super(name, price, otherInfo);
        }
        public void show(){
            System.out.println("**1类型车的信息:**");
            System.out.println("车名:"+getName());
            System.out.println("车价:"+getPrice());
            System.out.println("其他信息:"+getOtherInfo().toString());
        }
        
    }
    class Car2ConcretePrototype extends ICarPrototype{
        private static final long serialVersionUID =3L;
        public Car2ConcretePrototype(String name, int price,ArrayList<String> otherInfo) {
            super(name, price, otherInfo);
        }
        public void show(){
            System.out.println("##2类型车的信息:##");
            System.out.println("车名:"+getName());
            System.out.println("车价:"+getPrice());
            System.out.println("其他信息:"+getOtherInfo().toString());
        }
        
    }
    

    四、扩展

    1. 需要注意的是调用clone方法克隆java对象的时候不会调用构造器。
      这是因为执行clone方法的时候是直接从内存中去获取数据的,在第一次创建对象的时候就会把数据在内存保留一份,克隆的时候直接使用就好了
    2. 访问权限对原型模式无效
      由于数据从内存中直接复制的,所以克隆起来也会直接无视数据的访问权限,即使对于私有的数据也可以获取复制过来。

    五、参考

    原型模式
    深度好文:设计模式之——原型模式
    Java Object对象之clone方法
    设计模式之原型模式

    相关文章

      网友评论

        本文标题:设计模式之原型模式

        本文链接:https://www.haomeiwen.com/subject/njszuktx.html