常见设计模式

这篇具有很好参考价值的文章主要介绍了常见设计模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单例模式

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

单例模式的常见写法:

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

    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 中接口进行调用。

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

缺点:过多使⽤适配模式容易造成代码功能和逻辑意义的混淆。文章来源地址https://www.toymoban.com/news/detail-469203.html

到了这里,关于常见设计模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java的面向对象编程(oop)——static概述及初始单例设计模式

    过了入门阶段,开始学习进阶语法了。每天进步一点点,打好基础,daydayup! 什么是面向对象编程(oop),可以看这篇 java的面向对象编程(oop)概述及案例  static的意思为静态,用于修饰成员变量及成员方法。 成员变量根据有无static可以分为两种 ——类变量及实例变量 1,类

    2024年01月19日
    浏览(53)
  • 第08章_面向对象编程(高级)(static,单例设计模式,理解mian方法,代码块,final,抽象类与抽象方法,接口,内部类,枚举类,注解,包装类)

    回顾类中的实例变量(即非static的成员变量) 创建两个Circle对象: Circle类中的变量radius是一个实例变量(instance variable),它属于类的每一个对象,c1中的radius变化不会影响c2的radius,反之亦然。 如果想让一个成员变量被类的所有实例所共享,就用static修饰即可,称为类变量(

    2024年01月25日
    浏览(49)
  • 懒汉单例设计模式与饿汉单例设计模式

    单例模式即一个类确保只有一个对象,主要用于避免浪费内存 1 .饿汉单例设计模式 :拿到对象时,对象就早已经创建好了 写法: 把类的构造器私有 在类中自己创建一个对象,并赋值到一个变量 定义一个静态方法,返回自己创建的这个对象 2. 懒汉单例设计模式 :第一次拿到对象时

    2024年02月21日
    浏览(59)
  • 【设计模式】单例设计模式

    目录 1、前言 2、基本语法 2.1、懒汉式单例 2.2、饿汉式单例 2.3、双重检验锁单例模式 2.4、静态内部类单例模式 2.5、枚举单例模式 2.6、ThreadLocal单例模式 2.7、注册单例模式 3、使用场景 4、使用示例 5、常见问题 5、总结 单例模式是一种设计模式,它确保一个类只能创建一个实

    2024年02月09日
    浏览(44)
  • 设计模式学习(一)单例模式补充——单例模式析构

    目录 前言 无法调用析构函数的原因 改进方法 内嵌回收类 智能指针 局部静态变量 参考文章 在《单例模式学习》中提到了,在单例对象是通过 new 动态分配在堆上的情况下,当程序退出时,不会通过C++的RAII机制自动调用其析构函数。本文讨论一下这种现象的原因以及

    2024年03月19日
    浏览(53)
  • 【设计模式】单例模式|最常用的设计模式

    单例模式是最常用的设计模式之一,虽然简单,但是还是有一些小坑点需要注意。本文介绍单例模式并使用go语言实现一遍单例模式。 单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。 使用场景: 当类只能有一个实例而且可以从一个公开的众所周知的访

    2024年04月29日
    浏览(42)
  • 设计模式之单例设计模式

    就是一个类只允许创建一个对象,那么我们称该类为单例类,这种设计模式我们称为单例模式。 资源共享:有些类拥有共享的资源,例如数据库连接池、线程池、缓存等。使用单例模式确保只有一个实例,避免资源浪费和竞争条件。 线程安全:单例模式可以用来保证多线程

    2024年02月07日
    浏览(75)
  • 设计模式(单例模式)

            保证指定的类只有一个实例,不能创建出其他的实例                 1.1 代码展示                 1.2 Singleton类中instance对象的创建时机                 Singleton类中instance对象的创建时机:在Singleton类被jvm加载的时候创建,Singleton类会在第一次使用的时

    2024年02月15日
    浏览(51)
  • 设计模式-单例模式

          单例模式(Singleton Pattern)是设计模式中最简单且最常用的一种创建型模式,其目的是保证一个类在整个系统中只存在一个实例,并提供全局访问点来获取这个唯一实例。这种模式主要适用于那些需要频繁实例化然后又希望避免因为多次实例化而消耗过多资源或产生副

    2024年01月17日
    浏览(53)
  • 设计模式 ~ 单例模式

    单例模式:指在确保一个类只有一个实例,创建之后缓存以便继续使用,并提供一个全局访问点来访问该实例; 前端对于单例模式不常用,但对于单例的思想无处不在; 如:弹窗、遮罩层、登录框、vuex redux 中的 store; 闭包: 模块化:

    2024年02月16日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包