这篇文章主要讲解设计模式的知识,同时通过查看类图来揭秘设计并进行debug
创建型模式
工厂方法模式
定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行
适用场景:
- 创建对象需要大量重复代码
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 一个类通过其子类来指定创建哪个对象
优点:
- 用户只需关心所需产品对应的工厂,无须关心创建细节
- 加入新的产品符合开闭原则,提高可扩展性
缺点:
- 类的个数容易过多,增加复杂度
- 增加系统的抽象性和理解难度
从以上图中,可看出,Video是一个产品对象的抽象,其子类有JavaVideo、PythonVideo、FEVideo等子类,但是还有以下VideoFactory的抽象工厂,其子类有PythonVideoFactory、JavaVideoFactory、FEVideoFactory等子类来创建Video。很方便的进行扩展和使用,只需要将一个同是VIdeo的对象继承Video,再创建其工厂继承VideoFactory即可。
抽象工厂模式
定义:抽象工厂模式提供了一个创建一些列相关或相互依赖对象的接口
使用场景:
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一类产品族)一起使用创建对象需要大量重复的代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现
优点:
- 具体产品在应用层代码隔离,无须关心创建细节
- 将一个系列的产品族统一到一起创建
缺点:
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
- 增加了系统的抽象性和理解难度
抽象工厂模式:关注的是面向抽象编程,而且主要是针对那些固定的那种产品族进行设计
建造者模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
特征:用户只需指定需要建造的类型就可以得到它们,建造过程及细节不需要知道
适用场景:
- 如果对象有一个非常复杂的内部结构(很多属性)
- 想把复杂对象的创建和使用分离
优点:
- 封装性好,创建和使用分离
- 扩展性好、建造类之间独立、一定程度上解耦
缺点:
- 产生多余的Builder对象
- 产品内部发生变化,建造者都要修改,成本较大
单例模式
定义:保证一个类仅有一个实例,并提供一个全局访问点
使用场景:想确保任何情况下都绝对只要一个实例
优点:
- 在内存里只有一个实例,减少了内存开销
- 可以避免对资源的多重占用
- 设置全局访问点,严格控制访问
缺点:
- 没有接口,扩展困难
重点:
- 私有构造器
- 线程安全
- 延迟加载
- 序列化和反序列化安全
- 反射攻击
使用技能:
- 反编译
- 内存原理
- 多线程debug
单例和相关设计模式
- 单例模式和工厂模式
- 单例模式和享元模式
原型模式
定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
特点:不需要知道任何创建的细节,不调用构造函数
使用场景:
- 类初始化消耗较多的资源
- new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
- 循环体中产生大量对象
优点:
- 性能上比直接new一个对象性能高
- 简化创建过程
缺点:
- 必须配备克隆方法
- 对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
- 深拷贝、浅拷贝要运用得当
扩展:
- 深克隆
- 浅克隆
1 | package com.lzh.design.creational.prototype; |
结构性模式
适配器模式
定义:将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以一起工作
使用场景:
- 已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)
- 不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案
优点:
- 能提高类的透明性和服用,现有的类服用但不需要改变
- 目标类和适配器解耦,提高程序扩展性
- 符合开闭原则
缺点:
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性
- 增加系统代码可读的难度
扩展:
- 对象适配器模式
- 类适配器
装饰者模式
定义:在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)
使用场景:
- 扩展一个类的功能或给一个类添加附加职责
- 动态的给一个对象添加功能,这些功能可以动态的撤销
优点:
- 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能
- 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同的效果
- 符合开闭原则
缺点:
- 会出现更多的代码,更多的类,增加程序的复杂性
- 动态修饰时,多层装饰会更加复杂
代理模式
外观模式
定义:门面模式,提供了一个统一的接口,用来访问子系统中的一群接口。定义了一个高层接口,让子系统访问更加容易使用
使用场景:
- 子系统越来越复杂,增加外观模式提供简单调用接口
- 构建多层系统结构,利用外观对象作为每层的入口,简化层间调用
优点:
- 简化了调用过程,无需了解深入子系统,防止带来风险
- 减少系统依赖、松散耦合
- 更好的划分访问层次
- 符合迪米特法则,即最少知道原则
缺点:
- 增加子系统、扩展子系统行为容易引入风险
- 不符合开闭原则
相关设计模式:
- 外观模式和中介者模式
- 外观模式和单例模式
- 外观模式和抽象工厂模式
桥接模式
定义:将抽象部分与它的具体实现部分分离,使它们都可以独立的变化,通过组合的方式建立两个类之间的关系,而不是继承
使用场景:
- 抽象和场景之间增加更多的灵活性
- 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展
- 不希望使用继承,或因为多层继承导致系统类的个数增加
优点:
- 分离抽象部分和具体实现部分
- 提高了系统的可扩展性
- 符合开闭原则
缺点:
- 增加了系统的理解与设计难度
- 需要正确的识别出系统中两个独立变化的维度
组合模式
定义:将对象组合成树形结构以表示”部门-整体”的层次结构,使得客户端对单个对象和组合对象保持一致的方式处理
使用场景:
- 希望客户端可以忽略组合对象与单个对象的差异
- 处理一个树形结构
优点:
- 清除地定义分层次的复杂对象,表示对象的全部或部分层次
- 让客户端忽略层次的差异,方便对整个层次结构进行控制
- 简化客户端代码
- 符合开闭原则
缺点:
- 限制类型时会较为复杂
- 使设计变得更加抽象
享元模式
定义:提供了减少对象数量从而改善应用所需的对接结构的方式,运用共享技术有效的支持大量细粒度的对象
使用场景:
- 常常应用于系统底层的开发,以便解决系统的性能问题
- 系统有大量的相似对象、需要缓存池的场景
优点:
- 减少对象的创建、降低内存中的对象的数量、降低系统的内存、提高效率
- 减少内存之外的其他资源的占用
缺点:
- 关注内部和外部状态、关注线程安全问题
- 使系统程序的逻辑复杂化
扩展:
- 内部状态
- 外部状态