设计模式 概述
设计模式 是在实际开发中,根据特定 解决某种问题抽象出的一套设计方案。
为了提高代码的 复用性、可维护性和扩展性。
分类:创建型、结构型和行为型。
常见的设计模式
创建型模式:处理对象创建机制
特点:关注如何灵活的创建对象,隐藏细节,使代码更容易扩展。
-
单例模式:(Singleton)
保证一个类仅有一个实例,并且提供全局访问点。
场景:全局配置管理(如:Config类),日志工具(Logger),数据库连接池等,避免重复创建的资源消耗大的对象。
实现方式:私有构造函数,通过静态方法返回唯一实例(需要考虑线程安全)。 -
工厂模式: (Factory)
- 简单工厂:通过一个工厂类根据参数创建不同类型的对象.
- 工厂方法: 定义创建对象的接口,由子类决定具体实例化哪个对象.(如:日志工厂的FileLoggerFactory ConsoleLoggerFactory)。Spring中BeanFactory和ApplicationContext都是工厂模式,根据不同条件创建不同对象。
- 抽象工厂:创建一系列相关或互相依赖的对象,(如:跨平台UI库.同时创建按钮,文本框等配套组件).
场景:对创建逻辑复杂,需要屏蔽具体实现.(如:数据库驱动选择)
-
建造者模式:(Builder)
将复杂对象的构建过程与表示分离,分步构建对象(允许构建过程创建不同表示).
场景:创建多参数,多配置的对象.
优势:避免"telescoping constructor"(参数过多的构造函数),使代码清晰. -
原型模式:(Prototype)
通过复制现有对象(原型),创建新对象.避免重复初始化的开销.
场景:对象创建成本高(包含大量数据的DataModel),需要动态生成相似对象时(比如克隆罗有差异的配置对象),
结构型模式: 处理类或对象的组合.
关注类,对象的组合方式,实现更灵活的结构.
- 适配器模式:将一个类的接口转为客户端期望的另一种接口(如老系统接口适配新接口)
- 装饰器模式:动态给对象添加额外功能,不改变原有类(在原先功能上扩展新功能)
- 代理模式:为对象提供代理类,控制对原对象的访问(如远程代理、权限代理)与动态代理区分
- SpringAOP使用代理模式,调用方法前校验权限,有权限才能执行。实现对服务的控制。
- 组合模式:将对象组合成树型结构,统一处理单个对象(如文件系统的文件与文件夹)
- 外观模式:为复杂系统提供简化接口(如智能家居总控,封装灯光,空调等子系统)
行为模式:处理对象交互
关注对象间的通信与职责分配
- 观察者模式:定义对象间的一对多依赖,当一个对象变化时,依赖它的对象自动更新(如订阅-发布系统)
- 策略模式:定义算法家族,封装并动态切换(如购物车根据用户等级切换不同折扣策略)。
- 模板方法模式:父类定义算法骨架,子类实现具体步骤。
- Spring 的JdbcTemplate 是模板方法实现。定义来操作数据库的基本流程。具体实现有子类
- 迭代器模式:提供遍历集合元素的统一接口,不暴漏内部结构(如java中的Iterator)
- 命令模式:将请求封装为对象,支持参数化、队列i请求(如遥控器按钮对应不同的命令)
- 状态模式:对象状态改变时,动态切换行为(如订单状态”待支付-已支付-已发货“状态改变)
软件的设计原则
3大原则:接口功能(专一精简)、修改/扩展原则(具体可替换抽象)、类管理(避免跨类依赖)。保证”高内聚、低耦合“
1、掌握”设计原则“,设计模式是设计原则的具体体现。
- 单一职责原则:一个类负责单一功能。如:用户类不会应该同时处理支付类
- 违反:
- 类/方法臃肿,可读性差
- 职责耦合,修改时引发连锁Bug。多职责中,修改单一职责时,容易导致其他职责受影响。
- 复用性差,某职责 多处使用时,需要连同其他职责一同复用。
- 违反:
- 接口隔离原则:接口要小而专,避免 定义广泛。
- 违反:
- 接口臃肿,客户端被迫实现无关方法。
- 接口修改影响范围过大。
- 违反:
- 开放-封闭原则:对扩展开放,对修改关闭。如:新增功能时加代码,而不是修改原码。
- 违反:
- 修改原有代码,引入潜在Bug。
- 扩展性差,增加扩展成本。
- 团队协作冲突,同时修改原有代码。
- 违反:
- 里氏替换原则:子类可以替换父类,且不破坏原有逻辑。如:正方形不应该继承长方形,因为边长逻辑冲突。
- 违反:
- 程序逻辑异常,出现不可预期异常。子类违背父类的语义(比如重写方法时改变输入输出规则、抛出父类未声明的异常),会导致依赖父类的代码出错。
- 父类与子类耦合过高,难以维护。
- 违反:
- 依赖倒置原则:依赖抽象类,而不依赖具体实现。如:依赖支付接口,而不是依赖”微信/支付宝 支付“。
- 违反:
- 耦合度极高,扩展性差。
- 难以单元测试,
- 代码复用性差
- 违反:
- 迪米特法则:一个类只与直接相关类(依赖的类、成员变量的类)通信,减少耦合。如:避免A类中操作B类成员变量的成员变量。
- 违反:
- 耦合度高,修改影响范围大。
- 代码可读性低,逻辑难以追寻
- 测试难度增加。
- 违反:
2、理解不同设计模式,依据的设计原则。
问题场景 | 对应模式 | 核心作用 |
---|---|---|
如何控制对象创建(单例、灵活创建) | 单例、工厂方法、简单工厂 | 避免创建逻辑散落在业务代码中 |
如何灵活扩展对象功能(不修改原代码) | 装饰器、代理 | 动态添加功能,符合 OCP |
如何处理对象间的依赖 / 通信 | 观察者、依赖注入 | 解耦 “被依赖方” 和 “依赖方” |
如何封装算法 / 行为(动态切换) | 策略模式 | 避免用大量 if-else 判断算法 |
如何处理复杂对象的构建 | 建造者模式 | 分离 “对象构建” 和 “表示”(如复杂表单、对象有多个可选属性) |
3、学习方法:搞懂每个模式的3个问题。
- 解决什么痛点?(如单例解决 “多个地方创建同一类实例导致资源浪费”);
- 核心结构是什么?(不用死记 UML,但要知道 “有哪些角色”,如工厂模式有 “产品接口、具体产品、工厂接口、具体工厂”);
- 优缺点是什么?(如单例模式线程安全实现复杂,工厂模式会增加类的数量)。
创建型模式:灵活、可控的创建对象,避免硬编码new对象导致的耦合和资源浪费。
1、单例模式
解决:多处 重复创建无状态/重量级对象(如配置管理、线程池、数据库连接池),导致内存浪费,资源竞争(如多线程下连接池重复初始化)。
确保全局只有一个实例(如全局日志器,避免多实例写日志导致文件错乱)。
核心结构:
单例类:私有化构造方法(禁止外部new)、提供全局唯一获取实例的静态方法(如getInstance())、内部维护自身静态实例。
优缺点:减少内存开销,避免资源占用,全局同一访问点,状态易管理。
违背单一职责,线程安全实现复杂(双重检查锁),难以扩展(私有构造器)测试困难。
2、工厂方法
解决:
避免直接new具体对象,导致的耦合。
解决简单工厂模式下的扩展性问题。(简单工厂新增产品需要修改工厂类)
核心结构:4个核心角色
工厂接口:定义创建产品的抽象方法。
具体工厂:实现工厂接口,创建具体的产品。
产品接口:定义所有产品的统一行为。(仅定义单一产品如华为手机)
具体产品:实现产品接口的具体类。
优缺点:
符合开闭原则。代码解耦。
类数量较多,结构复杂,仅适用单一产品族(具体产品实现仅支持单一类)。
3、抽象工厂类
解决:
工厂方法模式只能创建单一产品的问题,指出创建产品族。(如华为手机+华为电脑 同一品牌下的不同产品)
避免产品族中产品不兼容(如华为工厂创建的手机和电脑,天然适配华为)
核心:
抽象产品接口:按产品类别拆分(如phone,computer,包含自身行为)
具体产品:实现抽象产品接口(华为phone、华为Computer、Iphone、IComputer)
抽象工厂接口:定义创建“产品族内所有产品”的方法(如AbstractFactory,含createPhone()、createComputer());
具体工厂:实现抽象工厂,创建对应产品族的所有产品(如HuaweiFactory创建HuaweiPhone+HuaweiComputer,AppleFactory创建IPhone+IComputer)。
优缺点:
保证产品族内产品的兼容性(同一工厂造的产品天然适配)。符合开闭原则。解耦产品。
扩展不易(需要扩展工厂),类数量多。
4、建造者模式
解决:
避免负责对象构造参数过多,导致代码混乱(如创建User对象需要10个参数,导致冗余)
分离“对象构造”和对象表示“
核心:
产品:需要构建的复杂对象
抽象建造者:定义构建产品各组件的抽象方法,
具体建造者:实现抽象建造者,完成具体组件的构建。
指挥者(可选):控制建造流程(如Director,调用建造者的方法按顺序组装,避免调用方关注步骤)。
优缺点:
分步构建复杂对象,参数清晰。统一建造者可生成不同产品。解耦构建过程。
只适用于复杂对象,类数量增多。构造流程变更时,需要修改Builder或指挥者(可能违背开闭)。