大概从刚学 Java 的时候就开始学设计模式了,但总是学了忘,忘了学。 其实容易忘记的原因是没有真正的去理解它,然后没有在工作中去用过,因为大部分的时间在处理业务,基本都是增删改查的东西,并且网上大部分的博客或者教程都是模拟场景,并且没有针对业务说明那种场景下可能会出现的问题。
举个例子: 以前学代理模式和装饰模式,都是要引入目标类然后调目标类的方法进行包装,感觉两个模式没区别啊,以现在的眼光来看,静态代理和装饰是没区别,但是动态代理和装饰的区别就大了,例如
那是不是在业务中就一定使用不到设计模式呢,不是,业务还是有场景和设计模式是符合的,但可能不是 100% 契合,需要你找到最合适的设计模式,像我最近碰到的一个业务场景
从 kafka 消费数据,但这个数据的处理过程有点麻烦,第一步需要存储整条消息到 mongodb , 第二步要存储到业务数据表,第三步算出消息要通知到系统中哪些用户(可以是短信通知和弹窗通知),第四步要根据终端设备的状态做对象处理 。 像这种就很适合责任链模式,后面的需求改动证明我的设计模式是对的,后面加了一个需求就是对每个设备发过来的消息有其自定义的数据,需要分别存库 ,我就在责任链中间加了一层处理就解决了,当中遇到了什么问题呢:
在网上看到过一种在业务中会出现的场景,也在这说一下,文章可能找不到了; 就是接入支付有几种渠道 微信、支付宝、银联 , 支付方式有 密码、人脸、指纹 ,如果传统写法就会有 3 * 3 个 if else ,这时这种笛卡尔积的就适合使用桥接模式 渠道聚合支付方式接口,可能不同渠道的支付方式实现会有小差异,可以使用设计模式 转换器 模式实现统一 , 最后代码可能就是这样子的 , 看起来就清爽易读了
// 使用微信的密码支付
new WeixinPayChannel(new PasswordAdapter(new WeixinPasswordPay())).transfer(args...);
总结一下 : 上面说到了常用的几种模式,动态代理、装饰、责任链,桥接,适配器,命令(主要是回退命令)
有一些模式是工作中,你可能无意识就使用到了,但你可能并不知道它是一种设计模式 ,比如 在使用 lombok 后,在一些属性比较多的类的习惯性的加了注解 @Builder ,你就已经使用了构建者模式了,只是这个构建者是 lombok 帮你实现了而已 ;再比如经常会对一些公共的东西往上抽,然后提取出一个类来让子类继承,然后在父类中调抽象方法但这个抽像方法是子类实现的,模板方法 就被你用上了;然后一些业务类,你总是习惯的加上 @Component 注解或 @Service 注解 ,其实这个类默认就是单例 ,单例模式 你也用上了,一些工具类但是使用的时候需要有一些配置,这个配置你可以使用懒加载在第一次使用的时候构建它,也是单例模式的运用; 然后像这样的需求,在应用启动后我要预热缓存,实现 ContextLoaderListener , kafka 来消息了要处理添加 @KafkaListener , Web 容器加载好了或者被销毁的时候要处理一些事情,实现 ServletContextListener 都是 观察者 的应用
总结一下:上面说到了 构建者、模板方法、单例,观察者
有些书上一开始就把单例介绍得很复杂,让看的人一下子就懵逼了,其实你大可以先绕过,你只要只到最简单的两种,直接创建的方式也叫饿汉式还有懒汉式两种即可。
饿汉式:类加载时直接创建实例,适用于创建对象不大并且创建不耗时的场景
public Singleton{
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance(){return instance;}
}
懒汉式:用的时候才加载,但需要考虑线程安全,需要同步锁
public Singleton{
private Singleton(){}
private static Singleton instance;
public synchronized static Singleton getInstance(){
if(instance == null){instance = new Singleton();}
return instance;
}
}
// 上面的做法效率低下,不管实例有没有创建,每个线程都要获取同步锁,所以有了改进的写法(也叫双重检验锁)
public Singleton{
private Singleton(){}
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
// 需要再判断一次的原因是在进入第一个判断有多个线程,不能重复创建实例
if(instance == null){instance = new Singleton();}
}
}
return instance;
}
}
静态内部类式:利用到了 Java 的类在使用时才会进行加载的原理,即保证了线程安全,又实现了懒加载
public Singleton{
private Singleton(){}
private static class Inner {static Singleton instance = new Singleton();}
public static Singleton getInstance(){return Inner.instance;}
}
以前一直觉得抽象工厂模式有点多余,感觉它把简单的事情搞复杂化了,简单工厂还好理解,抽象是真的不能理解 ,直到遇到了这个需求,实现一个对象池,每次从池中拿一个对象,需要拿未使用的对象,当然我可以借助 common-pool2 来实现,但是如果需要与 spring 结合,我就需要一个创建连接的工厂了,并且把这个工厂给 spring 容器来管理,这就是抽象工厂模式
总结一下: 上面说到了 工厂模式、抽象工厂模式
我在工作中遇到的就大概这向种了吧,可能源码中还有一些其它的模式还没熟悉,不过不熟悉的如果不了解应用场景的话,又会很快忘记,所以今天先总结到这了。
我的一个工具 sanri-tools , 可以做 kafka 监控(主题,消费组,分区,反序列化) , redis 数据查看(可以反序列化字段信息看到真实数据),数据表管理,代码生成
sanri-tools
我的博客文章大纲