zoukankan      html  css  js  c++  java
  • 05-日常工作日的一天:原型模式

    5.1模式背景故事

      以我们日常工作中朝九晚五的一天生活为背景对象。

      7:00——起床

      7:30——坐公交车

      8:30——早餐,到公司

      12:00——午餐,午休

      13:30——开始下午工作

      17:30——下班回家

    5.2模式定义

      原型设计模式(Prototype Pattern),用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

      本章讲述的是创建型模式中一种比较特殊的模式——原型模式,这个模式有个最大的特点就是克隆一个现有的对象,这个克隆的结果有两种,一种是浅复制,另一种是深复制。随后会探讨浅复制和深复制的原理。我们都知道创建型模式一般用来创建一个新的对象,然后使用这个对象完成一些对象的操作,我们通过原型模式可以快速地创建一个对象而不需要提供专门的new()操作,这无疑是一种非常有效的方式,可以快速创建一个新的对象。

    5.3模式分析

    5.3.1原型模式的静态建模

      原型设计模式的概念就是:用原型实例指定创建对象的种类,即我们一天工作日的生活,如果要生成多天的生活,只要通过复制这些原型创建新的对象就可以了,而不必每次都new()一个实例对象。这样将大大节省创建对象所消耗的各种资源。原型设计模式的结构图如下所示:

      

      在上面的原型模式结构图中,首先需要一个原型模式的抽象,接口和抽象类都行,在抽象中含有clone方法,具体的原型实现类具体实现抽象的clone方法,返回具体的类型。客户端使用抽象原型,传入的则是具体的原型对象类型,然后通过原型对象的clone方法产生一个与原型对象一样的克隆对象,而不需要使用new产生对象。

    5.4原型模式实现

    5.4.1原型的建立

      注:原型类必须实现Cloneable接口。

    package com.prototype.pojo;
    
    /**
     * Created by lsq on 2018/3/14.
     * 日常生活类
     */
    public class DayLife implements Cloneable{
    
        //起床
        private String getUp;
        //坐公交
        private String byBus;
        //下车,买早餐
        private String getFood;
        //中午小憩
        private String noon;
        //下午开始工作
        private String afternoonWork;
        //下班回家
        private String goHome;
        //晚上休闲
        private String night;
    
        public String getGetUp() {
            return getUp;
        }
    
        public void setGetUp(String getUp) {
            this.getUp = getUp;
        }
    
        public String getByBus() {
            return byBus;
        }
    
        public void setByBus(String byBus) {
            this.byBus = byBus;
        }
    
        public String getGetFood() {
            return getFood;
        }
    
        public void setGetFood(String getFood) {
            this.getFood = getFood;
        }
    
        public String getNoon() {
            return noon;
        }
    
        public void setNoon(String noon) {
            this.noon = noon;
        }
    
        public String getAfternoonWork() {
            return afternoonWork;
        }
    
        public void setAfternoonWork(String afternoonWork) {
            this.afternoonWork = afternoonWork;
        }
    
        public String getGoHome() {
            return goHome;
        }
    
        public void setGoHome(String goHome) {
            this.goHome = goHome;
        }
    
        public String getNight() {
            return night;
        }
    
        public void setNight(String night) {
            this.night = night;
        }
    
        /**
         * 打印输出日常生活信息
         */
        public void print(){
            System.out.println(this.getGetUp());
            System.out.println(this.getByBus());
            System.out.println(this.getGetFood());
            System.out.println(this.getNoon());
            System.out.println(this.getAfternoonWork());
            System.out.println(this.getGoHome());
            System.out.println(this.getNight());
        }
    
        /**
         * clone方法
         * @return
         * @throws CloneNotSupportedException
         */
        @Override
        public DayLife clone(){
            try {
                //调用超类的clone方法(所有类都是Object的子类)
                return (DayLife) super.clone();
            }catch (Exception e){
    
            }
            return null;
        }
    }

    5.4.2创建生成原型对象的工厂

    1)抽象工厂——ILifeFactory工厂类

    package com.prototype.factory;
    
    import com.prototype.pojo.DayLife;
    
    /**
     * Created by lsq on 2018/3/15.
     * 工厂类
     */
    public interface ILifeFactory {
    
        /**
         * 生产DayLife对象
         * @return
         */
        public DayLife getNewInstance();
    
    }

    2)具体工厂——LifeFactoryImpl工厂实现类

    package com.prototype.factory.impl;
    
    import com.prototype.factory.ILifeFactory;
    import com.prototype.pojo.DayLife;
    
    /**
     * Created by lsq on 2018/3/15.
     * 工厂实现类
     */
    public class LifeFactoryImpl implements ILifeFactory{
    
        //DayLife实例用于初始化
        private static DayLife dayLife = null;
    
        /**
         * getNewInstance方法实现:
         * 首先判断dayLife是否为null:
         * 如果是null,则使用new创建一个DayLife对象,同时设置初始内容,并赋值给dayLife对象实例,然后返回;
         * 如果不是null,则使用dayLife的clone方法产生一个新对象并复制给dayLife对象,然后返回
         * @return
         */
        @Override
        public DayLife getNewInstance() {
            //判断dayLife是否为null
            if (dayLife==null){
                //注意:new这个只使用一次
                System.out.println(" new DayLife !");
                dayLife = new DayLife();
                dayLife.setGetUp("7:00起床");
                dayLife.setByBus("7:30坐公交车");
                dayLife.setGetFood("8:30下公交,买早餐,去公司");
                dayLife.setNoon("12:00午餐,午休");
                dayLife.setAfternoonWork("13:30开始下午工作");
                dayLife.setGoHome("17:30下班回家");
                dayLife.setNight("晚上个人时间");
            }else {
                //输出使用clone方法产生的对象
                System.out.println(" clone DayLife !");
                dayLife = dayLife.clone();
            }
            return dayLife;
        }
    }

    5.4.3运行测试

    import com.prototype.factory.ILifeFactory;
    import com.prototype.factory.impl.LifeFactoryImpl;
    import com.prototype.pojo.DayLife;
    
    /**
     * Created by lsq on 2018/3/13.
     *
     */
    public class MainApp {
    
        public static void main(String[] args) {
            //创建工厂
            ILifeFactory lifeFactory = new LifeFactoryImpl();
            //打印输出DayLife默认内容
            lifeFactory.getNewInstance().print();
    
            //再次获得DayLife,修改起床时间
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~");
            DayLife dayLife = lifeFactory.getNewInstance();
            dayLife.setGetUp("早上赖床了,7:10才起床!");
            dayLife.print();
    
            //再次获得DayLife,修改起床时间
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~");
            DayLife dayLife2 = lifeFactory.getNewInstance();
            dayLife2.setGetUp("早上赖床了,7:20才起床!");
            dayLife2.print();
        }
    
    }

    运行结果:

    5.5设计原则

      原型模式的核心是一个clone方法,通过这个方法进行对象的复制,在Java中,提供了一个Cloneable接口来标示这个对象是可复制的。为什么说是“标示”呢?查看Cloneable类源码,会发现该接口中一个方法也没有,这个接口的作用就是一个标示,只有实现该接口的对象才有可能被复制!那,如何从“有可能被复制”变为“可以被复制”呢?方法就是覆盖超类的clone()方法!

      在使用原型模式的时候,要注意以下几个事项。

    1、克隆对象时,对象的构造方法不执行

      只有new时构造方法才被执行,clone的时候是不会执行构造方法的。为什么呢?因为在使用Object类的clone()方法时,是从内存中直接复制二进制流,重新分配内存块给克隆对象。

    2、浅复制和深复制

      先看一个例子:

    import java.util.ArrayList;
    
    /**
     * Created by lsq on 2018/3/15.
     *
     */
    public class Test implements Cloneable {
    
        //私有属性
        private ArrayList<String> nameList = new ArrayList<>();
    
        //添加内容
        public void add(String s){
            this.nameList.add(s);
        }
    
        //获得ArrayList对象
        public ArrayList get(){
            return this.nameList;
        }
    
        //clone方法
        @Override
        public Test clone(){
            try {
                return (Test)super.clone();
            }catch (CloneNotSupportedException e){
                e.printStackTrace();
            }
            return null;
        }
    
        public static void main(String[] args) {
            //创建test对象
            Test test = new Test();
            //设置test对象内容
            test.add("aa");
            test.add("bb");
            //打印显示test中的nameList内容
            System.out.println("test:"+test.get());
    
            //克隆test对象到test2对象
            Test test2 = test.clone();
            //添加“cc”到test2中
            test2.add("cc");
            //打印显示test2中的nameList内容
            System.out.println("test2:"+test2.get());
    
            //打印显示test中的nameList内容
            System.out.println("test:"+test.get());
        }
    }

    运行结果:

      我们发现对克隆对象的修改,影响到了原始对象的内容!这可不是我们想要看到的结果!这就是浅复制所带来的结果。

    1)浅复制

      Object类的clone方法只是复制对象的原始数据类型,如int、float、String等,对于数组和对象引用是不会复制的。因此,浅复制是有风险的。要做到完全复制,就需要深复制了。

    2)深复制

      深复制是指对于对象中的数组和对象引用也做复制的行为,从而达到将对象完全复制的效果。例如,上面的例子中,我们修改一下Test类的clone方法:

        //clone方法
        @Override
        public Test clone(){
            try {
                Test test = (Test)super.clone();
                test.nameList = (ArrayList<String>) this.nameList.clone();
                return test;
            }catch (CloneNotSupportedException e){
                e.printStackTrace();
            }
            return null;
        }

      运行结果:

      从结果来看,test和test2两个对象已经没有任何联系了。从以上内容来看,Object的clone方法只是做浅复制,也就是直接复制类的字段内容,并不管属性字段对象的内容。我们可以在重载的clone方法中进行类的“深复制”,从而得到我们想要的结果。

    5.6使用场合

      1)产生对象过程比较复杂,初始化需要许多资源;

      2)希望框架原型和产生对象分开时;

      3)同一个对象可能会供其他调用者同时调用访问时。

  • 相关阅读:
    一篇笔记整理JVM工作原理
    深入理解java异常处理机制
    Javaee----重新回顾servlet
    dubbo框架----探索-大型系统架构设计(图解)
    Java 中浮点数---------BigDecimal和double(初探)
    dubbo框架----初探索-配置
    ConcurrentHashMap-----不安全线程hashmap-安全线程-hashtable
    Maven Eclipse (m2e) SCM connector for subclipse 1.10 (svn 1.8) 无法检测
    从svn检出项目---------不是web项目
    Java当中的内存分配以及值传递问题内存解析
  • 原文地址:https://www.cnblogs.com/danielleee/p/8572814.html
Copyright © 2011-2022 走看看