专栏首页天马行空布鲁斯关于设计模式的那些事(一)

关于设计模式的那些事(一)

通常我们说的设计模式,指的是GoF23(Gang of Four),包括23个常用的设计模式。这里尝试从不同的角度聊一聊其中几个设计模式。

1. 单例模式

单例模式可能是一个程序员最早接触的设计模式之一,因为这个设计模式适用的场景非常广泛。比如,在Spring中,我们可以加注@Scope annotation来设置一个bean是”单例“或者”多例“,默认是单例。

单例模式有多种实现方式,其中常见的一种方式,如下:

public class Singleton {
    private static Singleton mySingleton;

    private Singleton() {
    }

    public synchronized Singleton getInstance() {
        if (null == mySingleton) {
            mySingleton = new Singleton();
        }
        return mySingleton;
    }
}

基于这种方式,每次调用getInstance都需要加锁,性能会受到很大的影响。所以,就有了下面这种基于双重检查锁的方式:

public class Singleton {
    private static Singleton mySingleton;

    private Singleton() {
    }

    public Singleton getInstance() {
        if (null == mySingleton) {
            synchronized (Singleton.class) {
                if (null == mySingleton) {
                    mySingleton = new Singleton();   // error
                }
            }
        }
        return mySingleton;
    }
}

这种(double checked locking)方式,解决了上面提到的性能问题。但是,由于JVM的指令重排序机制,上面的方式在某些情况下可能不工作,具体解释可以参考:

  • https://www.cnblogs.com/xz816111/p/8470048.html
  • https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

那么,解决方案就是在mySingleton变量申明前加上volatile(JDK1.5之后),如下:

public class Singleton {
    private volatile static Singleton mySingleton;

    private Singleton() {
    }

    public Singleton getInstance() {
        if (null == mySingleton) {
            synchronized (Singleton.class) {
                if (null == mySingleton) {
                    mySingleton = new Singleton();
                }
            }
        }
        return mySingleton;
    }
}

2. 代理模式

代理,对应的英文单词是”proxy“。提到代理,可能我们首先想到的是如果要访问google,需要设置一个代理,如下:

设置了这个代理之后,我们访问网址的所有请求,都会先经过这个代理服务器,然后由它帮忙转发到相应的网址服务器;处理完成之后,按原路返回给用户。代理服务器在转发请求之前,可能会做一些额外逻辑,比如:权限校验,限制只有付费用户才可以使用这个代理。

除此之外,我们可能还听过一个术语:反向代理reverse proxy,nginx就是一个流行的用于实现反向代理的开源软件;还有,在之前的一篇文章中(这些年我对微服务的理解)提到的API Gateway,其实也实现了reverse proxy的功能。所以,这里的proxy和上面的代理有些类似,都是帮忙转发请求到具体的请求处理服务器,并且对请求做一些额外的处理。

其实,代理模式原理上跟上面的例子类似,从编程角度讲,代理类在执行本来要代理的方法之前或者之后增加一些切面方法,以实现一些额外功能,比如打印日志等。

代理的实现,可以分为静态代理和动态代理。JDK提供了一个Proxy类,用于实现动态代理。Spring的AOP面向切面编程,底层就是基于JDK的动态代理或者CGLIB。

3. 观察者模式

简单讲,可以把观察者模式理解成事件监听机制,当一个事件发生时,触发所有提前注册好的监听方法。比如:Spring JPA的@PostPersist、@PostUpdate,当一个entity被持久化或者更新之后,加注了相应annotation的方法就会被执行。在很早之前用过JMX来实现Java进程的monitoring功能,JMX里面的Notification机制也即是基于观察者模式。还有,当我们点击UI上的一个button按钮,即会触发提前注册的相应callback方法,也是类似的原理。

同时,可以把观察者模式和现在常用的pub-sub模式做类比理解,它们有异曲同工之处。

4. 模版方法模式

我们平时工作中很多地方都有用到模版方法模式,比如:在Spring中,一个Bean的life cycle都会由容器负责执行init和destroy方法,而这两个方法可以在每个Bean定义的时候重写,这和c++里面类的构造方法、析构方法有些类似。同样的,SAP UI5里面的controller的life cycle也都会按顺序执行onInit、onBeforeRendering、onAfterRendering和onExit这几个方法,任何自己写的controller都可以重写。

5. 建造者模式

对于一个包含很多属性的复杂POJO,在创建一个对象的时候,我们调用setter方法去给相应的属性赋值,有时候代码会显得比较冗余,如下:

Person p = new Person();
p.setAttribute1("a1");
p.setAttribute2("a2");
p.setAttribute3("a3");
p.setAttribute4("a4");

这时我们就可以采用建造者模式,如下:

Person p = Person.builder().attribute1("a1").attribute2("a2").attribute3("a3").attribute4("a4").build();

一定程度来说,这种链式编程的方式显得简洁一些。如果使用Lombok,我们可以开箱即用的使用建造者模式,只需要在POJO类上加注一个@Builder annatation即可。

还有一个我们最熟悉的StringBuilder也是应用了这个模式,用于构造一个String对象。

References

  • https://docs.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/direct-client-to-microservice-communication-versus-the-api-gateway-pattern
  • https://projectlombok.org/features/Builder

本文分享自微信公众号 - 天马行空布鲁斯(gh_2feda5c053bd),作者:huazailmh

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-09-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 空谈系统架构设计之高并发、高可用

    对于一个应用系统,特别是互联网系统,高并发、高可用是两个非常重要的非功能性需求,这篇文章尝试从应用系统架构角度分析如何满足这两个特性。

    Bruce Li
  • 用antlr解析odata filter条件表达式

    我最早接触antlr,是在刚开始工作后不久,那次需要用antlr实现一个功能:把gemfire的OQL(object query language)翻译成SQL...

    Bruce Li
  • 关于html的input元素,property和attribute的区别

    之前在项目中遇到一个很tricky的关于html的input元素的问题,个人觉得挺有意思,于是记录下来。这个问题也是在ui的自动化测试中,可能会碰到的一个问题。

    Bruce Li
  • 如何创建一个RESTful WCF Service

    原创地址:http://www.cnblogs.com/jfzhu/p/4044813.html

    跟着阿笨一起玩NET
  • Eigen的基础使用

    #Eigen的安装 下载Eigen以后直接引用头文件即可,需要的头文件如下 ? Eigen支持的编译器类型 GCC, version 4.4 and new...

    Pulsar-V
  • Java 序列化

    我们经常在 java 中使用序列化,序列化成一个二进制文件,需要的时候再反序列化,但是一直只知道只要实现 Serializable 接口就可以了,一直不知道具体...

    haoming1100
  • AI们开始PK奥斯卡预测了:Google是降临派,亚马逊力挺爱乐之城

    量子位 | 问耕 发自 LZYY ? 明天一早,第89届奥斯卡就来了。8:00红毯环节开始,9:30颁奖典礼启幕。 这也意味着,关于这届奥斯卡小金人花落谁家的预...

    量子位
  • Using Stochastic Gradient Descent for classification使用随机梯度下降来分类

    As was discussed in Chapter 2, Working with Linear Models, Stochastic Gradient D...

    到不了的都叫做远方
  • 矩阵函数逼近的全局扩展理性Arnoldi方法(MATH.NA)

    诸如f(A)V的矩阵函数的数值计算出现在各种应用中,其中A是n×n的大而稀疏的方矩阵,V是具有ppn的n×p块,f是非线性矩阵函数例如网络分析(f(t)= ex...

    蔡小雪7100294
  • Django补充及初识Ajax

    Django创建一对多表结构 首先现在models.py中写如下代码: from django.db import models # Create your ...

    coders

扫码关注云+社区

领取腾讯云代金券