• 设计模式 | 4分钟搞懂10种设计模式


    虽然你觉得大炮肯定是个标题党,但你终究还是点进来了(别打我,手动狗头保命),毕竟这性价比也太高了,4分钟10种,如果是真的就赚大发了。

    img

    但是大炮可以肯定的告诉你,只要正儿八经的参与过几个项目,有个一两年的开发经验。你肯定已经用过并且可能已经理解了一些设计模式了,只是并不自知而已。今天大炮就来给你好好缕一缕。

    file

    单例、工厂模式

    这两个设计模式大家都不陌生,很多读者也都已经自学过了。比如饿汉、懒汉单例模式,简单、抽象工厂模式。而且这两个模式,就像兄弟一样,经常结对出现。所以大炮放到一起来讲,我们先来看一行代码:

    private static final Logger logger = LoggerFactory.getLogger(UserService.class);
    

    非常眼熟吧,这是我们平时用来获取日志对象的一段代码,但是重点并不是这个 logger 而是 LoggerFactory,顾名思义用来专门用来造 logger 的工厂 ,是一个工厂模式很好的体现。同时作为工厂呢,它又是单例的,毕竟new一个工厂就够了嘛,自己实例化那么多个干嘛呢,单例杜绝了反复创建和销毁对象的开销。

    同时它又用到了门面模式,这行代码是不是很牛,用到了三个设计模式,我们接着往下看。


    门面模式

    在《阿里巴巴Java开发手册》中,关于日志章节中专门有提到:

    【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

    import org.slf4j.Logger;

    import org.slf4j.LoggerFactory;

    private static final Logger logger = LoggerFactory.getLogger(Abc.class);

    有点感觉没,门面模式其实就是把自己作为一个接口,自己并不提供真正的实现。什么,还是没到位?那我们看看下面这段代码:

    img

    是不是又很眼熟了,下单要干挺多事情,保存订单,保存订单项,减库存…或者还有其他业务,如果所有这些方法的实现都放到 createOrder 这个方法里面,那代码肯定是又长又臭,洋洋洒洒就奔着几百上千行去了。所以有经验的你们肯定会抽到好几个子方法里面,就像上面一样,然后挨个调用下就行了。(PS,真正的下单代码肯定不是这么玩的,高并发下肯定挂了,看个意思就好)

    没错,这也是门面模式,createOrder 方法并没有给出具体的实现,直接调用了几个子方法而已。可能有些读者心里已经发出了嘲讽:**就这?就这?**这也算设计模式?我自己都会写啦。

    file

    没错,就这~设计模式就是对经验的总结,然后提出了一些建议和规范。其实并不是多么高深的东西。现在想想,如果给优秀的我们早生几年,有些设计模式的创造者还指不定是谁呢。回到上面的代码,虽然没多少技术含量,但对调用者而言,我管你怎么实现,我轻轻调一下 createOrder 方法就完事了。这正是门面模式的精髓所在。

    嗯,一行代码 get 到了三个设计模式。咱们继续。


    代理模式

    代理模式,是一个非常重要的设计模式,不理解它可能会让你直接无缘大厂。大家一定要学会并理解这个东西。我们先来看一段代码:

    file

    How old are you ? 怎么老是你 ? 没错,还是上面的图。这次的关注点大家放在 @Transaction 上。注解本身没啥用,只是作为一个标记,有时候会携带一些配置信息。老开发已经知道我要讲啥了,这里用到了Spring 中一个非常的重要特性:AOP编程。而AOP的实现原理就是代理模式。

    **代理模式的作用就是职责(或者说功能)增强。**那Spring帮我们做了哪些增强呢?上图的部分其实只有CRUD的业务代码,事务方面一行都没有。而没Spring之前,我们是要这样写的:

    try {    
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");    
        //建立连接     
        Connection con = DriverManager.getConnection()   
        //开启事务    
        con.setAutoCommit(true/false);    
        //真正的、我们每天都要写的:)    
        C R U D    
        //提交事务    
        con.commit()
    } catch (Exception e){   
        //失败了还要自己手动回滚    
        con.rollback();
    } finally {    
        //关闭连接    
        conn.close();
    }
    

    写的伪代码,不用在意细节。重要的是如果每个需要用到事务的方法都要这么重复写,我们估计早疯了。这里Spring就将这些重复的代码使用动态代理给我们抽到代理类中去了,所以我们只用关注CRUD就成——从这个角度看,一定程度上造成了程序员的懒惰,不是我们不学习,只怪Spring太好用。

    代理模式就不继续发散了,后面会精讲。


    桥接模式

    让我猜猜看,是不是有些同学第一次听到这个模式?猜对了待会下面给我留言点赞统计喔,但其实,我们每天都在用。咱们来看一段代码:

        //加载驱动    
        Class.forName("com.mysql.jdbc.Driver");    
        //建立连接     
        Connection con = DriverManager.getConnection()
    

    没错,大炮又套娃了,用的就是上面的连接数据库的代码。大家每天都要连接数据库的吧。

    先铺垫一个知识:Java 没有真正能操作数据库的方法,只是提供了一些抽象的接口,真正的实现在各个数据库厂商提供的相应jar包内。mysql就由mysql的开发者提供,oracle的就由oracle的提供。

    为什么我们这个DriverManager能直接get到Connection呢,就是使用了桥接模式。关键就在于这个Class.forName(“com.mysql.jdbc.Driver”),Class.forName 只是加载了一个类而已,怎么做到的呢。我们来看这个Driver类到底干啥了:

    file

    其实就是一个静态块里面调用了java.sql.DriverManager 的 registeDriver方法,然后把mysql 的Driver对象传了过去,起到了一个搭桥的作用。

    多的就不扩展了,桥接模式的作用就是连接了一个抽象维度和一个实现维度。这里的抽象维度就是指 java,实现维度就是指 mysql。其实也可以说桥接的精髓在于“约定”,java 和 各数据库厂商约定:你们底层实现我不管,但是你们得按照我的规矩来,调用我DriverManager的registerDriver方法才行。


    中介者模式

    聊完桥接模式,不得不聊下中介者模式了。桥接是关注两个维度的连接,而中介者是关注多个多个维度。作用是统一管理多个多个维度的网状资源。没错,可能你已经想到了,我们天天都在用的注册中心就是一个中介者,比如 eureka、zookeeper。

    file
    如图,所有的 service 都只和 Service Registry 耦合,而不是直接去依赖其他service ,中介者完美地解决了网状依赖的复杂关系带来的代码混乱。总体来说中介者还是个比较容易理解的设计模式,这里就不做太多展开了。


    解释器模式

    解释器模式是个非常简单的模式,有种自娱自乐的味道,如果你知道它是怎么自娱自乐的,那你也能跟着“娱乐一下”。来看一个熟悉的东东:

    #每隔五秒执行一次
    times = */5 * * * * ?
    

    你的配置文件是不是也有corn表达式呢,这就是一个解释器模式的体现。可能现在你的内心又:

    file

    **解释器模式定义了一种表达式(或者说是语言),并且也提供了解析表达式(语言)的方法。**类似的还有正则表达式。Spring中的ExpressionParser也用到了解释器模式,有兴趣的同学可以去看看源码。


    策略模式

    策略模式也比较容易理解,大家在开发和日常生活中也经常用到,也是一个需要重点掌握的设计模式。每天买东西,使用手机支付,就是策略模式的一种体现。**策略模式只注重结果,不管使用哪种策略(策略模式要求策略的数量是有限的),最后的结果都是一样的。**比如手机支付,不管使用哪种支付方式,该付多少还是得付多少。

    file


    原型模式

    原型模式也是一种非常简单,唉,凌晨一点多了。明天继续写吧。困了困了。

    file

    诶大炮又复活了。原型模式也是一种非常简单的设计模式:给定一个对象实例,通过拷贝的技术创建新的对象,达到对象复用的效果。来看一段代码:

    UserVo userVO = new UserVO();
    User user = mapper.selectByPrimaryKey(id);
    if(user == null){
        throw new Exception("未获取到指定用户");
    }
    /* 拷贝数据,传给前端大佬渲染页面 */
    BeanUtils.copyProperties(user,userVO);
    return userVO;
    

    是不是非常眼熟了,可能有些读者内心纷纷表示“是我本人没错了”。这里的BeanUtils.copyProperties 方法就用到了原型模式里面的 浅拷贝。

    稍微提一句,不要使用Apache的 BeanUtils 来copy。性能问题比较严重,在《阿里巴巴Java开发手册》中也有专门提到。如果需要切到Spring的copy方法,也有个坑需要注意下,这两家的提供的copy,类名相同、方法名也相同。但参数的位置相反,切换时请多加注意并做好测试。


    迭代器模式

    迭代器模式大家也天天都在用,直接上代码:

    file

    这是一段生成签名时,拼接参数的算法。里面的 Iterator 就是迭代器模式的体现,给集合提供了一个全局访问点,通过hasNext()判断没有下一个节点,next()方法获取下个节点,直到所有节点都被访问过。我们常用的 HashMap ,ArrayList 顶层都是 Collection,而 Collection又继承了 Iterator 接口。来张ArrayList 的类图感受下:

    file


    小结

    大炮稍微给大家缕了一遍,有些同学是不是觉得自己又行了,原来不知不觉间,自己竟然用了这么多设计模式,却不自知。其实还有很多,比如:

    • 责任链模式,Spring security、Shiro这些权限框架都有用到。
    • 观察者模式,Spring 的 ContextLoaderListener 就有用到。
    • 模板模式,Spring 中的 JdbcTemplate 有用到。

    设计模式无处不在,平时开发我们也无时无刻在享受着设计模式带来的好处,只是有些同学缺乏一个系统的认知。由于篇幅原因就不再过多介绍了。

    file

    好吧,标题党不算太过分,超了三分钟嘻嘻。

    下期预告

    今天讲的还是比较片面和粗犷的,很多细节都没有体现出来。这一期相当于是个设计模式的面试尝鲜版,下一期会对设计模式做一个正式的介绍:

    • 为什么要用设计模式
    • 设计模式是什么
    • 设计模式有哪些
    • 设计模式的分类

    大概就是上面这些了。然后就开始做具体某个模式的详细分析和代码实现(想先看什么模式记得私信我喔)。

    今天就先到这了,下次去面试被问到用过哪些设计模式,心里是不是就有底了,直接给他一顿连招13种统统安排上。我们下期见。

    转载 https://m.imooc.com/article/309749

  • 相关阅读:
    J2那几个E和Web基础
    PHP开发人员对JAVA的WEB开发入门(初版-基础知识)
    一个处理大数据的后台服务(已废弃)
    一个请求过来都经过了什么
    请一定记得升级java虚拟机
    面向对象之 结构体和类的区别
    Swift 结构体的使用
    iOS 波浪效果的实现
    iOS 常用三方(持续更新)
    Xshell 链接 Could not connect to '192.168.80.129' (port 22): Connection failed
  • 原文地址:https://www.cnblogs.com/yuluoxingkong/p/14803317.html
Copyright © 2020-2023  润新知