设计模式理论

90 阅读5分钟

前言

(等待补充实际应用)

设计模式的7个原则

  • 开闭原则:对扩展开放,对修改关闭
  • 单一职责: 一个类只负责一个功能领域的职责
  • 里氏替换:能引用基类的地方,必须能引用它的子类
  • 依赖倒置:依赖于抽象,不能依赖具体的实现
  • 接口隔离:类之间的依赖关系,应建立在最小的接口上
  • 聚合原则:尽可能使用聚合,而不是继承,来实现复用的目的
  • 最少知识:一个实体,尽可能避免与其他实体发生相互作用

分类

  • 创建型:创建时隐藏创建逻辑,工厂/单例/建造者/原型模式
  • 结构性:通过类与接口的继承/引用的实现来构造复杂的对象,适配器模式/桥接模式/过滤器/装饰器/代理模式等
  • 行为型:通过类之间不同的通讯方式来实现不同的行为。责任链/解释器/迭代器/观察者/状态/模板/访问者模式等

下面讲讲我理解中的常用的设计模式

1、工厂模式

1.1 简单工厂模式

客户端只需要提供参数,不需要关注对象具体的创建逻辑。

这种模式,适合对象较少的情况,如果需要另外增加新的产品,就需要修改工厂模式的底层判断逻辑,整体逻辑会变得复杂,且违反开闭原则。比如spring中的BeanFactory,就是根据传入的唯一标识来创建需要的bean对象的。

1.2 工厂模式

工厂模式将具体的产品任务分配给具体的产品工厂,也就是定义了一个抽象工厂和产品的生产接口,但不负责具体的产品。这样就不需要通过指定类型来创建对象了

1.3 抽象工厂模式

简单工厂模式和工厂模式都是针对某一类产品,而抽象工厂可以在AbstractFactory中增加产品的接口,并在具体的子工厂中实现产品的创建逻辑。新增产品,非常方便。

2 单例模式

单例模式,即只能创建一个实例,在对象创建的时候会产生线程安全问题,但是当创建出来之后,后面就不会出现线程安全的问题了(重点:构造方法私有化)

2.1 饿汉模式

顾名思义,类一加载,就创建对象。这种单例模式线程安全,不用加锁,执行效率较高;但是类加载的时候便初始化了,比较浪费内存空间。

image.png 但是,也可以使用反射的方法来调用它的构造方法,来建立第二个实例。 image.png

image.png

2.2 懒汉模式

单线程下的使用,完全没得问题,但是在多线程的环境下,无法保证单例,往往需要额外的操作。

image.png

为了保证线程安全,可以使用双重检查锁

image.png (注意,有volatile保证可见性与禁止重排序)

3 适配器模式

当需要两个不同的接口类之间通信的时候,在不改变类的前提下,就需要通过适配器进行通讯,增加了类的透明性和可复用性。但是过多的适配器容易造成代码功能和逻辑意义上的混乱。(没怎么用过)

4 代理模式

代理模式本质上是一个中间件,用来解耦服务的提供者和使用者。使用者通过代理间接访问服务的提供者,便于后者的封装和控制。

4.1 静态代理

静态代理需要针对每一个目标类都创建一个代理类,若接口需要增加新的方法,目标对象和代理对象都需要进行修改,非常麻烦。它在程序编译的时候,就已经将接口、实现类、代理类这些东西变成了一个个class文件,放到了jvm中。

4.2 动态代理

相对于静态代理,动态代理更加灵活,它不需要实现什么接口,直接可以代理实现类。动态代理在运行时动态生成类字节码,并加载到JVM中。

5 观察者模式

当一个对象状态发生变化时,所有该对象的关注者均能收到状态变化的通知,并进行相应的处理。在观察者模式中,观察者与被观察者是抽象耦合,耦合程度比较低,也正是因为如此,观察者只知道它发生了变化,但是却不知道变化的过程的原因。另外,观察链路可能非常长,花费的时间不好保证;有产生循环依赖的可能,将导致无限循环。

在实际项目中比较常见,比如在电商业务中,若观察到用户支付成功,则后面的执行逻辑,如更新订单业务、发送短信等。

6 装饰器模式

在不改变类对象的前提下,对类进行装饰,为类添加额外的功能。该过程是通过调用被包裹后的对象完成功能添加的,而不是直接修改原本的对象。(如果不能通过继承来增加功能,如类已经被final修饰,则可以通过装饰器模式来实现)(问题:与代理模式有什么本质上的区别?)

7 责任链模式

以某种顺序将处理者连接成一条链,所有的请求都严格按照顺序通过链上的处理者。就像 switch 语句。

8 策略模式

定义封装一系列算法、行为的对象,可以相互替换,就像if else语句那样