> 文章列表 > 面向对象设计原则-单一职责、开闭原则、里氏替换、依赖倒置、迪米特(最少知道原则)、接口隔离

面向对象设计原则-单一职责、开闭原则、里氏替换、依赖倒置、迪米特(最少知道原则)、接口隔离

面向对象设计原则-单一职责、开闭原则、里氏替换、依赖倒置、迪米特(最少知道原则)、接口隔离

前言

设计模式简述
六种面向对象设计原则说明、场景示例:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、迪米特(最少知道原则)、接口隔离原则
根据在线课程学习的记录,有些例子有所调整,如有错误或不准确的地方,欢迎指正

文章目录

    • 前言
    • 一、设计模式
      • 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)优点

  • 提高内聚,降低耦合
  • 提高系统的灵活性和维护性