常见设计模式

news2025/1/9 15:24:28

单例模式

单例对象的类必须保证只有一个实例存在,整个系统只能使用一个对象实例,优点:不会频繁地创建和销毁对象,浪费系统资源。缺点是没有抽象层,难以扩展。

单例模式的常见写法:

  • 饿汉式单例模式的写法:线程安全 ,顾名思义,类⼀加载就创建对象,这种⽅式⽐较常⽤,但容易产⽣垃圾对象,浪费内存空间。 优点:线程安全,没有加锁,执⾏效率较⾼缺点:不是懒加载(使⽤的时候再创建对象),类加载时就初始化,浪费内存空间。例如:游戏地图还没打开,地图实例已经加载导致内存拉满,手机卡顿。

    public class Singleton {
        // 1、私有化构造⽅法,避免外界直接new对象
        private Singleton(){}
        // 2、定义⼀个静态变量指向⾃⼰类型,私有对象避免直接用类获取
        private final static Singleton instance = new Singleton();
        // 3、对外提供⼀个公共的⽅法获取实例
        public static Singleton getInstance() {
            return instance;
        }
    }
    
  • 懒汉式单例模式的写法:非线程安全 ,优点:懒加载;缺点:线程不安全。

    • 目前此种方式的单例确实满足了懒加载,但是如果有多个访问者同时去获取对象实例你可以想象成一堆人在抢厕所,就会造成多个同样的实例并存,从而没有达到单例的要求。

      public class Singleton {
          // 1、私有化构造⽅法,不允许外部直接使用new创建
          private Singleton(){}
          // 2、定义⼀个静态变量指向⾃⼰类型
          private static Singleton instance;
          // 3、对外提供⼀个公共的⽅法获取实例
          public static Singleton getInstance(){//public后加上synchronized 就线程安全了,缺点效率低       
              // 判断为 null 的时候再创建对象
              if (instance == null) { //可能有多个线程同时访问到这行代码!
              	instance = new Singleton();
          	}
          return instance;
      }
      
  • 双重检查锁(DCL,即 double-checked locking )单例模式的写法:优点:懒加载,线程安全,效率较高;缺点:实现较复杂 。关于内部的第二重空判断的作用,当多个线程⼀起到达锁位置时,进行锁竞争,其中⼀个线程获取锁,如果是第⼀次进⼊则为 null,会进⾏单例对象的创建,完成后释放锁,其他线程获取锁后就会被空判断拦截,直接返回已创建的单例对象。

    public class Singleton {
        // 1、私有化构造⽅法
        private Singleton() {}
        // 2、定义⼀个静态变量指向⾃⼰类型
        private volatile static Singleton instance;
        // 3、对外提供⼀个公共的⽅法获取实例
        public static Singleton getInstance() {
            // 第一重检查是否为 null ,如果存在就不需要同步。
            if (instance != null) {
                return instance;
            }
            // 使⽤ synchronized 加锁
            synchronized (Singleton.class) {
                // 第二重检查是否为 null
                if (instance == null) {
                    // new关键字创建对象不是原⼦操作,123步骤
                    instance = new Singleton();
                }
            }
            return instance;
        }
    }
    

    双重检查锁中使用 volatile 的两个重要特性:可见性、禁止指令重排序;这是因为 new 关键字创建对象不是原⼦操作,创建⼀个对象会经历下⾯的步骤:

    1. 在堆内存开辟内存空间
    2. 调用构造方法,初始化对象
    3. 引用变量指向堆内存空间

    为了提高性能,编译器和处理器常常会对既定的代码执行顺序进行指令重排序,创建对象的执行顺序可能为 123或者132。当我们在引用变量上面添加 volatile 关键字以后,会通过在创建对象指令的前后添加内存屏障来禁止指令重排序,就可以避免这个问题,而且对volatile 修饰的变量的修改对其他任何线程都是可见的。不加volatile 导致DCL失效问题:某个线程乱序运⾏ 1 3 2 指令的时候 ,引用变量指向堆内存空间,这个对象不为 null,但是没有初始化,其他线程有可能这个时候进⼊了 getInstance 的第⼀个 if(instance == null) 判断不为 nulll ,导致错误使⽤了没有初始化的⾮ null 实例,这样的话就会出现异常。

  • 枚举单例,优点:简单,⾼效,线程安全,可以避免通过反射破坏枚举单例 。

    public enum Singleton {
        INSTANCE;
        public void doSomething(String str) {
        	System.out.println(str);
        }
    }
    Singleton singleton = Singleton.INSTANCE;
    

工厂模式

简单工厂模式:简单工厂模式又叫静态工厂方法模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建 。就是用工厂方法代替new操作的一种模式,可以给系统带来更大的可扩展性和尽量少的修改量(降低耦合)。要什么问工厂拿,而不自己new,如果工厂升级了只需要修改工厂。客户端不需要关注创建逻辑,只需提供传入工厂的参数 。缺点是如果要增加新产品,就需要修改工厂类的判断逻辑,违背开闭原则,且产品多的话会使工厂类比较复杂。 例如咖啡机!

  • 优点:客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可 ;客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品; 实现了责任分割。

  • 缺点:不易拓展,一旦添加新的产品类型,就不得不修改工厂的创建逻辑 。产品类型较多时,工厂的创建逻辑可能过于复杂,一旦出错可能造成所有产品的创建失败,不利于系统的维护

  • Calendar 抽象类的 getInstance 方法,调用createCalendar 方法根据不同的地区参数创建不同的日历对象。

  • Spring 中的 BeanFactory 使用简单工厂模式,根据传⼊⼀个唯⼀的标识来获得 Bean 对象。

工厂方法模式:和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式具体的产品工厂生产具体的产品只需要定义⼀个抽象工厂,其定义了产品的生产接口,但不负责具体的产品,将生产任务交给不同的派生类工厂,这样不用通过指定类型来创建对象了。

抽象工厂模式

简单工厂模式和工厂方法模式不管工厂怎么拆分抽象,都只是针对⼀类产品,如果要生成另⼀种产品,就比较难办了!抽象工厂模式是在简单工厂的基础上将未来可能需要修改的代码抽象出来,通过继承的方式让子类去做决定 ,以上面的咖啡工厂为例,某天我的口味突然变了,不想喝咖啡了想喝啤酒,这个时候如果直接修改简单工厂里面的代码,这种做法不但不够优雅,也不符合软件设计的“开闭原则”,因为每次新增品类都要修改原来的代码。这个时候就可以使用抽象工厂类了,抽象工厂里只声明方法,具体的实现交给子类(子工厂)去实现,这个时候再有新增品类的需求,只需要新创建代码即可。

代理模式

代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。(为真实对象提供代理,然后供其他对象通过代理访问真实对象)。

  • 静态代理:以租房为例,租客找房东租房,然后中间经过房屋中介。租客类和代理类都需要实现了租房接口,这就是一个静态代理的前提,那就是真实类和代理类要实现同一个接口,在代理类中实现真实类的方法同时可以进行真实类方法的增强处理。
  • 动态代理:当接口增加新方法,目标对象和代理对象都需要修改,这是非常麻烦的。所以就引进动态代理,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建⼀个代理类。动态代理更加灵活 ,实现原理:JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
  • JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了⼀个个实际的 class ⽂件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

静态代理只能够对一种类型进行代理,如果想要对多种类型进行代理的话就需要创建多个代理类,为了弥补了静态代理的不足,从而出现了动态代理,使用反射技术实现,它是由java.lang.reflect下面的ProxyInvocationHandler进行支持的。

观察者模式

观察者模式又叫做发布-订阅(Publish/Subscribe)模式 。观察者模式主要用于处理对象间的⼀对多的关系,是⼀种对象行为模式。该模式的实际应用场景比较容易确认,当⼀个对象状态发⽣变化时,所有该对象的关注者均能收到状态变化通知,以进行相应的处理。

优点:被观察者和观察者之间是抽象耦合的 ;被观察者无需关心他的观察者 ;支持广播通信;

缺点:观察者只知道被观察对象发生了变化,但不知变化的过程和缘由 ;被观察者也可能是观察者,如果观察者和被观察者之间产生循环依赖,或者消息传递链路形成闭环,会导致无限循环。

示例:支付场景:用户购买⼀件商品,当支付成功之后三方会回调自身,在这个时候系统可能会有很多需要执行的逻辑(如:更新订单状态,发送邮件通知,赠送礼品…),这些逻辑之间并没有强耦合,因此天然适合使用观察者模式去实现这些功能,

模板方法模式

模板方法模式是指定义一个算法骨架,将具体内容延迟到子类去实现。 Spring 中 jdbcTemplate 、 hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使⽤到了模板模式。 例如:喝茶分为三步:烧水、放入茶叶泡茶、品茶;那么烧水和品茶相同代码可以父类实现,而具体泡什么茶交给子类实现。

优点:提高代码复用性:将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中; 实现了反向控制:通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制并且符合开闭原则。

适配器模式

在我们的应用程序中我们可能需要将两个不同接口的类来进行通信,在不修改这两个的前提下我们可能会需要某个中间件来完成这个衔接的过程。这个中间件就是适配器。所谓适配器模式就是将⼀个类的接口,转换成客户期望的另⼀个接口:有点像欧标充电器适配器。它可以让原本两个不兼容的接口能够无缝完成对接。通过类继承实现适配,继承 Target 的接口,继承 Adaptee 的实现。

Target:定义 Client 真正需要使⽤的接口;Adaptee: 其中定义了⼀个已经存在的接口,也是我们需要进行适配的接口。Adapter: 对 Adaptee 和 Target 的接口进⾏适配,保证对 target 中接口的调⽤可以间接转换为对 Adaptee 中接口进行调用。

优点:组合若⼲关联对象形成对外提供统⼀服务的接口;提高了类的复用(不用再造新类);

缺点:过多使⽤适配模式容易造成代码功能和逻辑意义的混淆。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/602606.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免安装版MySQL数据库的安装和卸载

说明:MySQL早些版本有分安装版和免安装版,可在官网(https://dev.mysql.com/downloads/mysql/)下载,推荐使用最新版本,是免安装版的,下载完配置一下就可以用。 推荐使用最新版本 安装 我这里以…

第3章“程序的机器级表示”:过程

文章目录 3.7 过程3.7.1 栈帧3.7.2 转移控制3.7.3 寄存器使用惯例3.7.4 过程示例3.7.5 递归过程 3.7 过程 一个过程调用包括将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。另外,它还必须在进入时为过程的局部变量分…

金融人不能错过的中国人民大学与加拿大女王大学金融硕士,你不能不知道

金融行业是一个发展飞速的行业,越来越多的优秀人士的涌入,让本就卷起来的金融行业变得异常拥挤,怎么办,想留有一席之地只能不断的提升与攀登,金融人不能错过的中国人民大学与加拿大女王大学金融硕士,你不能…

架构EA演进

架构演进 目录概述需求: 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive. happy for hardess to solve den…

spring cloud搭建(service)

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

【C++】海量数据处理面试题(位图和布隆过滤器)

都是大厂面试题哦~ 文章目录 一.位图面试题 1.给定100亿个整数,设计算法找到只出现一次的整数 2.给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集? 3.1个文件有100亿个int,1G内…

等价类,边界值,场景法的使用方法和运用场景

等价类: 在很多情况下,很多人想到的测试方法是穷举测试,穷举测试是最全面的测试,但是数据量很大的情况下不太现实,测试效率太低,后来为了减少测试人员的工作量和提高测试的效率和以达到最好的测试质量&…

启明星辰集团CEO严望佳:与AI共生,共建以人为本的数字善治生态体系

近日,2023中国国际大数据产业博览会在贵阳成功召开。启明星辰集团董事长兼首席执行官严望佳应邀出席大会“数据安全产业高质量发展”高端对话,发表“主动应对ChatGPT技术冲击,加强数据安全风险防控”主题演讲,同与会人士共探数据安…

32.有序序列插入一个整数(刷题)

描述 有一个有序数字序列,从小到大排序,将一个新输入的数插入到序列中,保证插入新数后,序列仍然是升序。 输入描述: 第一行输入一个整数N(0≤N≤50)。 第二行输入N个升序排列的整数,输入用空格分隔的N个…

2023年前端面试题汇总-浏览器原理

1. 浏览器安全 1.1. 什么是 XSS 攻击? 1.1. 1. 概念 XSS 攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。 XSS 的本质是因为网站没有对…

企业要从哪些方面着手进行数据安全治理?

什么是数据安全治理? 数据安全治理是指组织基于业务发展与合规要求,制定全面且系统的数据安全策略、流程与技术措施,对数据生命周期中的安全风险进行管控与优化的一系列管理活动。它需要从组织层面建立数据安全管理框架,保证敏感数…

2023-6-2-DIS研究

🍿*★,*:.☆( ̄▽ ̄)/$:*.★* 🍿 💥💥💥欢迎来到🤞汤姆🤞的csdn博文💥💥💥 💟💟喜欢的朋友可以关注一下&#xf…

Java 列表导出

一、具体实现 import java.net.URLEncoder; import com.alibaba.excel.EasyExcel;List<实体> targets xxx; response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("UTF-8"); String fileName URLEncoder.encode(&…

深入理解设计原则之里氏替换原则(LSP)

系列文章目录 C高性能优化编程系列 深入理解设计原则系列 深入理解设计模式系列 高级C并发线程编程 LSP&#xff1a;里氏替换原则 系列文章目录1、里氏替换原则的定义和解读2、里氏替换原则可以用于哪些设计模式中&#xff1f;3、如何使用里氏替换原则来降低代码耦合度&#…

Julia系列14:调用自定义C/C++库

1. 基础调用&#xff1a;ccall 调用的基本格式为&#xff1a; ccall((:函数名, 库地址), 输出格式, (输入格式列表), 输入数据) 下面是例子&#xff1a; 1.1 基础数据结构 1.2 数组 首先是输入数组&#xff0c;注意需要convert 接着是输出数组&#xff0c;需要使用unsafe…

《操作系统》—— 处理机调度算法

前言&#xff1a; 在之前的文章中&#xff0c;我们已经了解了进程和线程相关的基本概念&#xff0c;今天我们将要了解的是关于处理机调度相关的知识。 目录 &#xff08;一&#xff09;调度的概念 1、调度的基本概念 2、调度的层次 3、三级调度的关系 &#xff08;二&…

遗传算法(Genetic Algorithm)

本文为阅读《遗传算法原理及应用》的笔记和心得 ISBN&#xff1a;7-118-02062-1 遗传算法简介 遗传算法是模拟生物在自然环境中的遗传和进化过程中而形成的一种自适应全局优化概率搜索算法 总的来说&#xff0c;求最优解解或近似最优解的方法主要有三种&#xff1a;枚举法、启…

【PCB专题】Allegro设置禁止铺铜区域但仍可以走线和打过孔

在PCB设计中我们有时候需要做一些净空区,但是净空区内有一些走线和过孔。如果使用Route Keepout画一个框的话,那是不允许走线和打过孔的,会报DRC。 那么如何才能既禁止区域铺铜,又可以走线和打过孔不报DRC呢? Setup->Areas->Shape Keepout Options选择要禁止…

第二十一篇、基于Arduino uno,控制有源蜂鸣器和无源蜂鸣器发出声音——结果导向

0、结果 说明&#xff1a;有源蜂鸣器按照一定的频率报警&#xff0c;无源蜂鸣器则是一直报警&#xff0c;都采用非阻塞方式编写&#xff0c;如果是你想要的&#xff0c;可以接着往下看。 1、外观 说明&#xff1a;有源蜂鸣器和无源蜂鸣器看上去一样&#xff0c;但是背面不一…

详解Handler

详解Handler 文章目录 详解Handler1.Handler的工作流程1.1主线程具有如上性质的原因1.2流程图 2.Handler流程中的重要的几个方法2.1Message中的属性2.2.1what2.2.2replyTo2.2.3obtain 2.2Handler.post()与Handler.sendMessage()2.2.1post的源码2.2.1.1sendMessageDelayed()源码…