• 设计模式—创建型模式之原型模式


    设计模式—创建型模式之原型模式

    原型模式(Prototype Pattern)用于创建重复的对象,同时又能保证性能。

    本体给外部提供一个克隆体进行使用。

    比如我们做一个SjdwzMybatis,用来操作数据库,从数据库里面查出很多记录,其中很多记录改变很少。每次查数据库,把所有数据都封装一个对象,然后返回。假设有很多线程,来查如下记录:

    Student student = new Student("张三","男");
    

    如果每次都创建对象封装并返回,这样系统就会有很多student;这样就会浪费内存。

    Student类如下:

    public class Student {
        private String name;
        private Integer age;
    
        public Student() {
            System.out.println("创建了Student对象");
        }
        //省略getter() 、 setter() toString()
    }
    
    

    SjdwzMybatis如下:

    public class SjdwzMybatis {
    
        /**
         * 通过name获取Student
         */
        public Student queryStudent(String name){
            return queryStudentFromDB(name);
        }
    
        /**
         * 演示从数据库查Student
         */
        private Student queryStudentFromDB(String name) {
            //简单演示,查询到了
            System.out.println("从数据库查询到了:"+name);
            Student student = new Student();
            student.setName(name);
            student.setAge(16);
            return student;
        }
    }
    

    测试类:

    public class ProtoTypeTest {
        public static void main(String[] args) {
            SjdwzMybatis sjdwzMybatis = new SjdwzMybatis();
            Student stu1 = sjdwzMybatis.queryStudent("zhangsan");
            Student stu2 = sjdwzMybatis.queryStudent("zhangsan");
            Student stu3 = sjdwzMybatis.queryStudent("zhangsan");
            Student stu4 = sjdwzMybatis.queryStudent("zhangsan");
        }
    }
    

    这样会有大量具有相同属性的student被外部创建,同时查库次数过多。

    我们是否能设计一个缓存,来保存查过的内容,再查相同的记录时,可以很快拿到原来的原型对象呢?

    那我们的SjdwzMybatis便变成了如下代码:

    public class SjdwzMybatis {
    	//缓存
        private Map stuCache = new HashMap<>();
    
        /**
         * 通过name获取Student
         */
        public Student queryStudent(String name){
            if(stuCache.containsKey(name)){
                return stuCache.get(name);
            }else{
                return queryStudentFromDB(name);
            }
        }
    
        /**
         * 演示从数据库查Student
         */
        private Student queryStudentFromDB(String name) {
            //简单演示,查询到了
            System.out.println("从数据库查询到了:"+name);
            Student student = new Student();
            student.setName(name);
            student.setAge(16);
            //存入内存
            stuCache.put(name,student);
            return student;
        }
    }
    

    但是这是否会有问题呢?

    修改属性

    如果我们把stu1的属性改了,那么stu2、stu3、stu4的属性也会被改变,这会影响到我们缓存里的数据,造成脏缓存数据;同时我们查出来的内容,并没有提交修改,不能就把原数据给修改掉。

    原型模式

    我们把Student修改成如下代码,这便是原型模式:

    //实现Cloneable接口,这只是一个标记,还需要重写clone()方法
    public class Student implements Cloneable{
        private String name;
        private Integer age;
    
        //重写clone方法
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Student student = new Student();
            student.setName(this.name);
            student.setAge(this.age);
            return student;
        }
    }
    

    然后SjdwzMybatis修改为如下代码:

    public class SjdwzMybatis {
        //缓存
        private Map stuCache = new HashMap<>();
    
        /**
         * 通过name获取Student
         */
        public Student queryStudent(String name) throws CloneNotSupportedException {
            if(stuCache.containsKey(name)){
                return (Student) stuCache.get(name).clone();
            }else{
                return queryStudentFromDB(name);
            }
        }
    
        /**
         * 演示从数据库查Student
         */
        private Student queryStudentFromDB(String name) throws CloneNotSupportedException {
            //简单演示,查询到了
            System.out.println("从数据库查询到了:"+name);
            Student student = new Student();
            student.setName(name);
            student.setAge(16);
            //存入内存
            stuCache.put(name,(Student) student.clone());
            return student;
        }
    }
    

    从数据库查出来放入缓存的对象与从缓存取出来的都是clone出来的。

    可以看到,我们对stu1修改,并不会影响其他的数据了。

    效果

  • 相关阅读:
    MySQL |子查询
    猿创征文|ZooKeeper(伪)集群搭建
    【多线程的线程安全问题(常见的四种线程安全问题)】【两种解决:synchronized、volatile】
    05 Python Numpy moivepy 生成MP4 爱满一颗心
    Flink系列之Flink基础使用与核心概念
    【PCIE703】基于华为海思ARM的8路SDI高清视频图像处理平台(KU060+HI3531D)
    Leecode刷题 Day7----------哈希表
    使用wpf mvvm light sqlsugar 搭建增删改框架
    开发者地图
    应遵循哪些 GitLab 备份最佳实践
  • 原文地址:https://www.cnblogs.com/nicaicai/p/17796364.html