Java程序员必备知识——常用设计模式:单列+装饰器+策略+观察者

Java程序员必备知识——常用设计模式:单列+装饰器+策略+观察者

一、单列模式

1、懒汉(线程不安全,懒加载):如果没有实例就创建并返回,会有线程安全问题,简单加synchronized解决,但效率不高,升级版本是DCL。

public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static Singleton getInstance() {
if (instance null) {
instance = new Singleton();
}
return instance;
}
}

2、懒汉的升级DCL【双重检索】(线程安全,懒加载):加两个if是因为可能会有多个线程一起进入同步块外的if,如果在同步块内不进行二次检验的话就可能会生成多个实例;instance = new Singleton()这句会有重排序的问题,用volidate解决。

public static Singleton getSingleton() {
if (instance null) {
//Single Checked
synchronized (Singleton.class) {
if (instance null) {
//Double Checked
instance = new Singleton();
//有JVM重排序问题

}
}
}
return instance ;
}
//防止JVM指令重排序
public class Singleton {
private volatile static Singleton instance;
//声明成 volatile,禁用JVM指令重排序
private Singleton (){
}
public static Singleton getSingleton() {
if (instance null) {
synchronized (Singleton.class) {
if (instance null) {
instance = new Singleton();
}
}
}
return instance;
}
}

3、饿汉(线程安全,非懒加载):利用类加载器保证线程安全,但不是懒加载。

public class Singleton{
//类加载时就初始化,无法从外部文件设置属性,如从properties文件设置属性
private static final Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}

4、饿汉升级->静态内部类(线程安全,懒加载):在饿汉的基础上使用静态内部类解决非懒加载问题。

public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

5、枚举(线程安全,非懒加载):写法最简单,可以防止反序列化,但不是懒加载。

public enum EasySingleton{
INSTANCE;
}

总结:一般情况下直接使用饿汉式就好了,如果明确要求要懒加载会倾向于使用静态内部类,如果涉及到反序列化创建对象时会试着使用枚举的方式来实现单例。

Java程序员必备知识——常用设计模式:单列+装饰器+策略+观察者

二、装饰器模式

在保持原有功能接口不变的基础上动态扩展功能的模式。 ​ Java IO包中就使用了该模式,InputStream有太多的实现类如FileInputStream,如果要在每个实现类上加上几种功能如缓冲区读写功能Buffered,则会导致出现FileInputStreamBuffered, StringInputStreamBuffered等等,如果还要加个按行读写的功能,类会更多,代码重复度也太高,你说改原来的接口也行啊,但是这样就是改变接口的内容了,现在我想做到不更改以前的功能,动态地增强原有接口。 所以使用FilterInputStream这个抽象装饰器来装饰InputStream,使得我们可以用BufferedInputStream来包装FileInputStream得到特定增强版InputStream,且增加装饰器种类也会更加灵活。

缺点:会引入很多小类,从而增加使用复杂度,多层装饰的时候很容易导致不知道如何使用

Java程序员必备知识——常用设计模式:单列+装饰器+策略+观察者

三、策略模式

一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

比如利用策略模式优化过多 if else 代码,将这些if else算法封装成一个一个的类,任意地替换:

Java程序员必备知识——常用设计模式:单列+装饰器+策略+观察者

整体思路如下

  • 定义一个 InnerCommand 接口,其中有一个 process 函数交给具体的业务实现。
  • 根据自己的业务,会有多个类实现 InnerCommand 接口;这些实现类都会注册到 Spring Bean 容器中供之后使用。
  • 通过客户端输入命令,从 Spring Bean 容器中获取一个 InnerCommand 实例。
  • 执行最终的 process 函数。

缺点

  • 策略类会增多
  • 所有策略类都需要对外暴露。
Java程序员必备知识——常用设计模式:单列+装饰器+策略+观察者

四、观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息

Java程序员必备知识——常用设计模式:单列+装饰器+策略+观察者

可以看到,该模式有四个角色:

  • 抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
  • 抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  • 具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,向所有登记过的观察者发出通知。
  • 具体观察者角色:实现抽象观察者角色所需要的更新接口,以便使本身的状态与制图的状态相协调。

优点

  • 松偶合、易维护。改变主题或观察者中的一方,另一方不会受到影像。

缺点

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

写在最后

Java架构师丨苏先生:专注于Java开发技术的研究与知识分享!

————END————

  • 转发(好咖啡要和朋友一起品尝,好文章也要和朋友一起分享!)
  • ...
  • 关注(持续更新)
  • ...

Java程序员必备知识——常用设计模式:单列+装饰器+策略+观察者


分享到:


相關文章: