面向对象设计原则-单一职责、开闭原则、里氏替换、依赖倒置、迪米特(最少知道原则)、接口隔离
前言
设计模式简述
六种面向对象设计原则说明、场景示例:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、迪米特(最少知道原则)、接口隔离原则
根据在线课程学习的记录,有些例子有所调整,如有错误或不准确的地方,欢迎指正
文章目录
-
- 前言
- 一、设计模式
-
- 1. 概念
- 2. 解决什么问题
- 3. 三大类型
- 4. 设计模式和设计原则的关系
- 二、设计原则
-
- 1. 单一职责
- 2. 开闭原则
- 3. 里式替换
- 4. 依赖倒置
- 5. 迪米特
- 6. 接口隔离
一、设计模式
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 {@Overridepublic void payMoney() {System.out.println("微信支付...");}}/*** 支付宝支付*/static class AliPay implements IPay {@Overridepublic 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);}@Overridedouble 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 {@Overrideint add(int a, int b) {return a - b;}}
-
修正
class Son extends FourArithmeticOperations {@Overrideint 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 {@Overridepublic void drive() {System.out.println("驾驶宝马车...");}}static class BenzCar implements ICar {@Overridepublic 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)优点
- 提高内聚,降低耦合
- 提高系统的灵活性和维护性