常见设计模式介绍
原型模式
原型模式是指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
适用场景
- 类初始化消耗资源较多
- 使用new生成一个对象需要非常烦琐的过程(数据准备、权限访问等)
- 构造函数比较复杂
- 在循环体中产生大量对象
运用场景
Spring中 scope = "prototype"
,以及JSON.parseObject()
也是原型模式
代理模式
代理模式在客户端与目标对象之间起到中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
代理分为:静态代理、动态代理、CGLib代理
CGLib 和 JDK 动态代理对比
- JDK 动态代理实现了被代理对象的接口, CGLib 代理继承了被代理对象 。
- JDK 动态代理和 CGLib 代理都在运行期生成字节码,JDK 动态代理直接写 Class 字节码, CG Lib 代理使用 ASM 框架 写Class 字节码, CGlib 代理实现更复杂,生成代理类比 JDK 动态代理效率低
- JDK 动态代理调用代理方法是通过反射机制调用的, CGLib 代理是通过 FastClass 机制 直接调用方法的, CGLib 代理的执行效率更高。
Spring 中的代理选择原则
- 当 Bean 有实现接口时, Spring 就会用 JDK 动态代理。
- 当 Bean 没有实现接口时, Spring 会选择 CGLib 代理。
静态代理和动态代理的本质区别
- 静态代理只能通过于动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则。
- 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
- 若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无须修改代理类的代码。
代理模式的优缺点
优点:
- 代理模式能将代理对象与真实被调用目标对象分离。
- 在一定程度上降低了系统的耦合性,扩展性好。
- 可以起到保护目标对象的作用。
- 可以增强目标对象的功能。
缺点:
- 代理模式会造成系统设计中类的数量增加。
- 在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢。
- 增加了系统的复杂度。
委派模式
委派模式( Delegate Pattern )不属于 GoF 23 种设计模式。委派模式的基本作用就是负责任务的调用和分配,跟代理模式很像,可以看作一种特殊情况下的静态的全权代理,但是代理模式注重过程,而委派模式注重结果。
代理模式、委托模式、策略模式之间区别
-
代理模式注重 是过程 委派模式注重的是结果
-
策略模式注重可扩展性(外部扩展性),委派模式注重内部的灵活性和可复用性
-
委派模式的核心就是分发、调度、派遣,委派模式是静态代理和策略模式的一种特殊组合
策略模式
策略模式( Strategy Pattern )是指定义了算法家族并分别封装起来,让它们之间可以互相替换,此模式使得算法的变化不会影响使用算法的用户。
应用场景
- 系统中有很多类,而它们的区别仅仅在于行为不同。
- 一个系统需要动态地在几种算法中选择一种。
运用场景
JDK的Comparator接口中的 compare()
方法就是一个策略模式的抽象实现。
策略模式的优缺点
优点:
- 策略模式符合开闭原则。
- 策略模式可避免使用多重条件语句,如 if… else 语句、 switch 语句。
- 使用策略模式可以提高算法的保密性和安全性。
缺点:
- 客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
- 代码中会产生非常多的策略类,增加了代码的维护难度。
模板模式
模板模式又叫模板方法模式(Template Method Pattern),是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现 。模板模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤,属于行为型设计模式。
如:炒菜流程:洗菜–刷锅–点火–放油–放材料–炒菜,其中某些步骤可以固定不改变,但是流程是一样的。
适用场景
- 一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。
模板模式的优缺点
优点:
- 利用模板模式将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。
- 将不同的代码放到不同的子类中,通过对子类的扩展增加新的行为,可以提高代码的扩展性。
- 把不变的行为写在父类中,去除子类的重复代码 ,提供了一个很好的代码复用平台 ,符合开闭原则。
缺点:
- 每个抽象类都需要一个子类来实现,导致了类的数量增加。
- 类数量的增加间接地增加了系统的复杂性。
- 因为继承关系自身的缺点,如果父类添加新的抽象方法,所有子类都要改一遍。
适配器模式
适配器模式( Adapter Pattern )是指将一个类的接口转换成用户期望的另一个接口,使原本接口不兼容的类可以一起工作,属于结构型设计模式。
应用场景
- 已经存在的类的方法和需求不匹配(方法结果相同或相似)的情况。
- 适配器模式不是软件初始阶段考虑的设计模式,是随着软件的发展,由于不同产品、不同厂家造成功能类似而接口不同的问题的解决方案,有点亡羊补牢的感觉。
适配器模式的优缺点
优点:
- 能提高类的透明性和复用性,现有的类会被复用但不需要改变。
- 目标类和适配器类解稿,可以提高程序的扩展性。
- 在很多业务场景中符合开闭原则。
缺点:
- 在适配器代码编写过程中需要进行全面考虑 ,可能会增加系统的复杂性。
- 增加了代码的阅读难度,降低了代码的可读性,过多使用适配器会使系统的代码变得凌乱。
装饰者模式
装饰者模式( Decorator Pattern )是指在不改变原有对象的基础上,将功能附加到对象上,提 供了比继承更有弹性的方案(扩展原有对象的功能),属于结构型模式。装饰者模式在生活中的应用也比较多,如给煎饼加鸡蛋、给蛋糕加 些水果、给房子装修等,都是在为对象扩展一些额外的职责。
适用场景
- 扩展一个类的功能或给一个类添加附加职责。
- 动态给一个对象添加功能,这些功能可以再动态地撤销。
装饰者模式和适配器模式对比
装饰者模式和适配器模式都是包装模式(Wrapper Pattern),装饰者模式也是一种特殊的代理模式。
装饰者模式 | 适配器模式 | |
---|---|---|
形式 | 是一种非常特别的适配器模式 | 没有层级关系,装饰者模式有层级关系 |
定义 | 装饰者和被装饰者实现同一个接口,主要目的是扩展后依旧保留OOP关系 | 适配器和被适配者没有必然的联系,通常采用继承或代理的形式进行包装 |
关系 | 满足is-a关系 | 满足has-a的关系 |
功能 | 注重覆盖、扩展 | 注重兼容、转换 |
设计 | 前置考虑 | 后置考虑 |
装饰者模式的优缺点
优点:
- 装饰者模式是继承的有力补充,且比继承灵活,可以在不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
- 使用不同的装饰类及这些装饰类的排列组合,可以实现不同的效果。
- 装饰者模式完全符合开闭原则。
缺点:
- 会出现更多的代码、更多的类,增加程序的复杂性。
- 动态装饰时,多层装饰会更复杂。
观察者模式
应用场景
观察者模式( Observer Pattern )定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知井更新,属于行为型模式。观察者模式有时也叫作发布订阅模式 观察者模式主要用于在关联行为之间建立一套触发机制的场景。观察者模式在现实生活应用得也非常广泛,比如微信朋友圈动态通知、邮件通知、广播通知等。
观察者模式的优缺点
优点:
- 在观察者和被观察者之间建立了一个抽象的耦合。
- 观察者模式支持广播通信。
缺点:
- 观察者之间有过多的细节依赖、时间消耗多,程序的复杂性更高。
- 使用不当会出现循环调用。
各种设计模式的总结与对比
GoF 23种设计模式
设计模式其实是一门艺术。设计模式来源于生活,不要为了套用设计模式而使用设计模式 设计模式是在我们迷茫时提供的一种解决问题的方案,或者说用好设计模式可以防范于未然。
设计模式是经验之谈,总结的是前人的经验,提供给后人借鉴使用, 正所谓: “前人栽树, 后人乘凉。”设计模式可以帮助我们提升代码的可读性、可扩展性,降低维护成本,解决复杂的 业务问题,但是千万不要死记硬背,生搬硬套。
GoF 23种设计模式的归纳和总结
分类 | 设计模式 |
---|---|
创建型 | 工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式 |
结构型 | 适配器模式、桥接模式、组合模式、装饰者模式、门面模式、享元模式、代理模式 |
行为型 | 解释器模式、模板方法模式(模板模式〕、责任链模式、命令模式、法代器模式、调解者模式、 备忘录模式、观察者模式、状态模式、策略模式、访问者模式 |
设计模式之间的关联关系
单例模式和工厂模式
在实际业务代码中,通常会把工厂类设计为单例模式。
策略模式和工厂模式
- 工厂模式包含工厂方法模式和抽象工厂模式,是创建型模式,策略模式属于行为型模式。
- 工厂模式的主要目的是封装好创建逻辑,策略模式接收工厂创建好的对象,从而实现不同的行为。
策略模式和委派模式
- 策略模式是委派模式内部的一种实现形式,策略模式关注结果是否能互相替代。
- 委派模式更关注分发和调度的过程。
模板方法模式和工厂方法模式
工厂方法模式是模板方法模式的一种特殊实现,工厂方法模式相当于只有一个步骤的模板方法模式。
模板方法模式和策略模式
- 模板方法模式和策略模式都有封装算法。
- 策略模式使不同算法可以相互替换,且不影响客户端应用层的使用。
- 模板方法模式针对定义一个算法的流程,将一些有细微差异的部分交给子类实现。
- 模板方法模式不能改变算法流程,策略模式可以改变算法流程且可替换。策略模式通常用来替换if…else等条件分支语句。
装饰者模式和代理模式
- 装饰者模式的关注点在于给对象动态添加方法,而代理模式更加注重控制对象的访问。
- 代理模式通常会在代理类中创建被代理对象的实例,而装饰者模式通常会把被装饰者作为构造参数。
- 装饰者和代理者虽然都持有对方的引用,但处理重心是不一样的。
装饰者模式和适配器模式
- 装饰者模式和适配器模式都属于包装器模式(Wrapper Pattern)。
- 装饰者模式可以实现与被装饰者相同的接口,或者继承被装饰者作为它的子类,而适配器和被适配者可以实现不同的接口。
- 适配器模式主要解决兼容问题,不一定要统一父类。装饰者模式需要满足OOP的is-a的关系。
适配器模式和静态代理模式
适配器模式可以结合静态代理模式来实现,保存被适配对象的引用,但不是唯一的实现方式。
适配器模式和策略模式
在业务复杂的情况下,可利用策略模式优化适配器模式。
模式都属于包装器模式(Wrapper Pattern)。
2. 装饰者模式可以实现与被装饰者相同的接口,或者继承被装饰者作为它的子类,而适配器和被适配者可以实现不同的接口。
3. 适配器模式主要解决兼容问题,不一定要统一父类。装饰者模式需要满足OOP的is-a的关系。
适配器模式和静态代理模式
适配器模式可以结合静态代理模式来实现,保存被适配对象的引用,但不是唯一的实现方式。
适配器模式和策略模式
在业务复杂的情况下,可利用策略模式优化适配器模式。