前言
设计模式简述
六种面向对象设计原则说明、场景示例:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、迪米特(最少知道原则)、接口隔离原则
根据在线课程学习的记录,有些例子有所调整,如有错误或不准确的地方,欢迎指正
一、设计模式
1. 概念
- 一套被反复使用、多数人知晓的、经过分类编目的代码设计经验的总结
- 解决特定问题的通用模板
- 一套针对特定的场景总结出来的解决方案
2. 解决什么问题
- 创建对象:针对对象创建过程中遇到问题的解决方案
- 继承、组合类:针对使用类的继承,组合过程中遇到问题的解决方案
- 交互:针对对象之间交互过程程中遇到问题的解决方案
3. 三大类型
- 创建类型
- 更好、更优地创建类或对象
- 结构型
- 将类和对象组合,构建更好的结构
- 行为型
- 更好地实现类与类、对象与对象之间的通信(方法调用)
4. 设计模式和设计原则的关系
- 理论——设计原则,实践——设计模式
- 设计模式开始时,以原则为指导
- 设计模式结束时,以原则来衡量
二、设计原则
设计思想:高内聚、低耦合
高内聚:一个模块或类的内部功能和职责是高度一致的
低耦合:模块或类之间的关系和联系的强度是低的
示例理解:企业划分多个部门,分工协作
1. 单一职责
1)定义
- 一个类或者模块应当有且仅有一个引起它变化的原因
- 单一:作用的类或模块
- 职责:引起变化的原因
2)场景示例
支付中处理多种支付方式,如微信支付,支付宝支付,如果在同一个类中处理多种支付方式,当其中一种支付方式需要调整,可能会影响到其他支付方式的调用
-
一个支付类中处理两种支付方式,不符合单一职责
/** * 支付类 */ class Pay{ /** * 付钱 * @param type 支付类型 */ public void payMoney(String type) { if (Objects.equals(type, "wechat")) { System.out.println("使用微信支付..."); } else if (Objects.equals(type, "ali")) { System.out.println("使用支付宝支付..."); } } }
-
优化
/** * 支付接口 */ interface IPay { void payMoney(); } /** * 微信支付 */ static class WechatPay implements IPay { @Override public void payMoney() { System.out.println("微信支付..."); } } /** * 支付宝支付 */ static class AliPay implements IPay { @Override public void payMoney() { System.out.println("支付宝支付..."); } }
-
调用
IPay pay = new WechatPay(); pay.payMoney(); pay = new AliPay(); pay.payMoney();
3)优点
- 降低类的复杂度
- 提高代码的可读性
- 提高代码的维护性
- 降低变更引起的风险
2. 开闭原则
1)定义
- 一个软件实体应当对扩展开放,对修改关闭
- 开:应对变化
- 闭:保持稳定
2)场景示例
汽车价格不会固定不变,比如当有促销时,则会进行打折,直接修改原获取价格方法,则可能影响到原本在用的功能
-
初始获取价格
/** * 汽车 */ class Car { double price; public Car(double price) { this.price = price; } /** * 获取价格 */ double getPrice() { // 打5折 return this.price * 0.5; } }
-
优化
/** * 打折汽车 */ class DiscountCar extends Car { public DiscountCar(double price) { super(price); } @Override double getPrice() { return super.getPrice() * 0.5; } }
-
调用
Car car = new DiscountCar(100000); System.out.println("打折后价格:" + car.getPrice());
3)优点
-
提高代码的可测性
-
提高代码的可维护性
3. 里式替换
子类替换父类
1)定义
- 派生类(子类)对象可以在程式中代替其基类(超类)对象
- 替换:所有引用父类的地方都可以替换为子类
- 一致:子类替换父类的程序逻辑保持一致
2)场景示例
四则运算类中包含加减乘除,父类中加法,而子类实现却覆盖成减法
-
错误示例
/** * 四则运算 */ class FourArithmeticOperations { /** * 加法 * * @param a * @param b * @return */ int add(int a, int b) { return a + b; } int minus(int a, int b) { return a - b; } } class Son extends FourArithmeticOperations { @Override int add(int a, int b) { return a - b; } }
-
修正
class Son extends FourArithmeticOperations { @Override int add(int a, int b) { return a + b; } }
3)优点
- 保证行为的正确性
- 提升代码的健壮性
4. 依赖倒置
细节依赖于抽象,抽象不依赖于细节
1)定义
- 高层模块不应该依赖底层模块,两者都应该依赖其抽象
- 抽象不依赖细节
- 细节应该依赖于抽象
- 面向接口编程
2)场景示例
拿到C1驾照后,可以开宝马轿车、奔驰轿车。司机和具体的某一品牌车不绑定,而是和准驾车绑定
-
初始代码
class Driver { void drivingBmw() { System.out.println("驾驶宝马汽车..."); } void drivingBenz() { System.out.println("驾驶奔驰汽车..."); } }
-
优化
interface ICar { /** * 开车 */ void drive(); } static class BmwCar implements ICar { @Override public void drive() { System.out.println("驾驶宝马车..."); } } static class BenzCar implements ICar { @Override public void drive() { System.out.println("驾驶奔驰车..."); } }
-
调用
Driver driver = new Driver(); driver.drive(new BmwCar());
3)优点
- 降低耦合性
- 提高稳定性
- 减少并行开发带来的风险
5. 迪米特
最少知道原则
1)定义
- 一个对象应当对其他对象尽可能少的了解
- 不和陌生人说话
- 只与直接的朋友通信
2)场景示例
租客和房东不直接交流,而是和中介交流;同理房东也是和中介交流
-
有的租客和房东交流,有的租客和中介交流,若是同一套房子和中介、房东成交了,将出现问题
/** * 房东 */ class HouseOwner { void bargain() { System.out.println("谈价格:1000元/月"); System.out.println("花费时间:1周"); } } /** * 中介 */ class Intermediary { void bargain() { System.out.println("谈价格:1200元/月"); System.out.println("花费时间:1天"); } } /** * 租客A */ class TenantA { void bargain(){ System.out.println("找房东..."); System.out.println("找中介..."); } } /** * 租客A */ class TenantB { void bargain(){ System.out.println("找中介..."); } }
3)优点
- 降低耦合性
- 提高复用性
6. 接口隔离
建立单一接口
1)定义
- 类之间的依赖关系应该建立在最小的接口
- 不强迫使用不同的方法
- 识别使用者的角色,设计小接口
2)场景示例
士兵中有开坦克攻击、开飞机轰炸,还有用枪突进,但是在进攻中每个士兵各司其职,并非又会开飞机又开坦克攻击
-
初始
interface ISoldier { /** * 开飞机轰炸 */ void bombingByPlane(); /** * 开坦克碾压 */ void rollingWithTanks(); /** * 开枪进攻 */ void shootWithGun(); }
-
优化:不同兵种抽象
/** * 航空兵 */ interface AirArm { void bombingByPlane(); } /** * 坦克兵 */ interface TankForce { void rollingWithTanks(); } /** * 步兵 */ interface infantry { void shootWithGun(); }
3)优点
- 提高内聚,降低耦合
- 提高系统的灵活性和维护性