设计模式原则——里氏替换原则

设计模式原则

设计模式示例代码库地址:

https://gitee.com/Jasonpupil/designPatterns

里氏替换原则

  • 继承必须确保父类所拥有的性质在子类中依然成立

  • 与开闭原则不同的是开闭原则可以改变父类原有的功能,里氏替换原则不能修改父类的原有的性质,即使子类扩展了父类的功能,也不能改变父类的原有功能

  • 提高兼容性、维护性和扩展性

    • 子类和父类的接口保持一致,确保在任何使用父类的地方都可以替换为子类,而不会影响系统功能
    • 子类能够无缝地替换父类,替换时不需要修改客户端代码,方便地扩展新功能而不需要对现有代码进行大规模修改
    • 由于子类完全遵循父类的契约,系统在替换子类时不容易出现未预见的运行时错误

使用场景:银行卡存储,

  • 信用卡继承并重写储蓄卡的功能,破坏了里氏替换原则,根据里氏替换原则进行修改

里氏替换原则替换前示例代码:

储蓄卡类:
java 复制代码
/**
 * @Description: 模拟储蓄卡功能
 * @Author: pupil
 * @Date: 2024/06/23 下午 10:04
 */
public class CashCard {

    private Logger logger = LoggerFactory.getLogger(CashCard.class);

    private static List<String> tradeList = new ArrayList<>();

    /**
     * 提现
     *
     * @param orderId 单号
     * @param amount  金额
     * @return 状态码 0000成功、0001失败、0002重复
     */
    public String withdrawal(String orderId, BigDecimal amount) {
        // 模拟支付成功
        logger.info("提现成功,单号:{} 金额:{}", orderId, amount);
        return "0000";
    }

    /**
     * 储蓄
     *
     * @param orderId 单号
     * @param amount  金额
     * @return 状态码 0000成功、0001失败、0002重复
     */
    public String recharge(String orderId, BigDecimal amount) {
        // 模拟充值成功
        logger.info("储蓄成功,单号:{} 金额:{}", orderId, amount);
        return "0000";
    }

    /**
     * 交易流水查询
     *
     * @return 交易流水
     */
    public List<String> tradeFlow() {
        logger.info("交易流水查询成功");
        tradeList.add("14451,100.00");
        tradeList.add("14451,65.00");
        tradeList.add("14451,76.50");
        tradeList.add("14451,126.00");
        return tradeList;
    }
}
信用卡类:
java 复制代码
 /**
   * @Description: 模拟信用卡功能,重写父类的方法,违法里氏替换原则
   * @Author: pupil
   * @Date: 2024/06/23 下午 10:32
   */
  public class CreditCard extends CashCard{
  
      private Logger logger = LoggerFactory.getLogger(CashCard.class);
  
      /**
       * @param orderId 单号
       * @param amount  金额
       * @return 状态码 0000成功、0001失败、0002重复
       */
      @Override
      public String withdrawal(String orderId, BigDecimal amount) {
          // 校验
          if (amount.compareTo(new BigDecimal(1000)) >= 0){
              logger.info("贷款金额校验(限额1000元),单号:{} 金额:{}", orderId, amount);
              return "0001";
          }
          // 模拟生成贷款单
          logger.info("生成贷款单,单号:{} 金额:{}", orderId, amount);
          // 模拟支付成功
          logger.info("贷款成功,单号:{} 金额:{}", orderId, amount);
          return "0000";
      }
  
      /**
       *
       * @param orderId 单号
       * @param amount  金额
       * @return 状态码 0000成功、0001失败、0002重复
       */
      @Override
      public String recharge(String orderId, BigDecimal amount) {
          // 模拟生成还款单
          logger.info("生成还款单,单号:{} 金额:{}", orderId, amount);
          // 模拟还款成功
          logger.info("还款成功,单号:{} 金额:{}", orderId, amount);
          return "0000";
      }
  
      @Override
      public List<String> tradeFlow() {
          return super.tradeFlow();
      }
  }
测试类:
java 复制代码
/**
   * @Description: 验证测试
   * @Author: pupil
   * @Date: 2024/06/23 下午 10:33
   */
  public class ApiTest {
  
      private Logger logger = LoggerFactory.getLogger(ApiTest.class);
  
      @Test
      public void test_CashCard() {
          CashCard cashCard = new CashCard();
          // 提现
          cashCard.withdrawal("14451", new BigDecimal(100));
          // 储蓄
          cashCard.recharge("14451", new BigDecimal(100));
          // 交易流水
          List<String> tradeFlow = cashCard.tradeFlow();
          logger.info("查询交易流水,{}", JSON.toJSONString(tradeFlow));
      }
  
      @Test
      public void test_CreditCard() {
          CreditCard creditCard = new CreditCard();
          // 支付
          creditCard.withdrawal("14451", new BigDecimal(100));
          // 还款
          creditCard.recharge("14451", new BigDecimal(100));
          // 交易流水
          List<String> tradeFlow = creditCard.tradeFlow();
          logger.info("查询交易流水,{}", JSON.toJSONString(tradeFlow));
      }
  }
结果:

里氏替换原则替换后示例代码:

银行卡类:
java 复制代码
/**
* @Description: 银行卡
* @Author: pupil
* @Date: 2024/06/23 下午 10:46
*/
public abstract class BankCard {

  private Logger logger = LoggerFactory.getLogger(BankCard.class);
  private static List<String> tradeList = new ArrayList<String>();

  private String cardId;   // 卡号
  private String cardDate; // 开卡时间

  public BankCard(String cardId, String cardDate) {
      this.cardId = cardId;
      this.cardDate = cardDate;
  }

  abstract boolean rule(BigDecimal amount);


  /**
   * 正向入账,+ 钱
   * @param orderId 单号
   * @param amount 金额
   * @return 状态码 0000成功、0001失败、0002重复
   */
  public String positive(String orderId, BigDecimal amount) {
      // 入款成功,存款、还款
      logger.info("卡号{} 入款成功,单号:{} 金额:{}", cardId, orderId, amount);
      return "0000";
  }

  /**
   * 逆向入账,- 钱
   * @param orderId
   * @param amount
   * @return 状态码 0000成功、0001失败、0002重复
   */
  public String negative(String orderId, BigDecimal amount) {
      // 入款成功,存款、还款
      logger.info("卡号{} 出款成功,单号:{} 金额:{}", cardId, orderId, amount);
      return "0000";
  }

  /**
   * 交易流水查询
   *
   * @return 交易流水
   */
  public List<String> tradeFlow() {
      logger.info("交易流水查询成功");
      tradeList.add("14451,100.00");
      tradeList.add("14451,80.00");
      tradeList.add("14451,76.50");
      tradeList.add("14451,126.00");
      return tradeList;
  }

  public String getCardId() {
      return cardId;
  }

  public String getCardDate() {
      return cardDate;
  }
}

/**
* @Description: 模拟储蓄卡功能
* @Author: pupil
* @Date: 2024/06/23 下午 10:04
*/
public class CashCard extends BankCard {

  private Logger logger = LoggerFactory.getLogger(CashCard.class);

  public CashCard(String cardNo, String cardDate) {
      super(cardNo, cardDate);
  }

  boolean rule(BigDecimal amount) {
      return true;
  }

  /**
   * 提现
   *
   * @param orderId 单号
   * @param amount  金额
   * @return 状态码 0000成功、0001失败、0002重复
   */
  public String withdrawal(String orderId, BigDecimal amount) {
      // 模拟支付成功
      logger.info("提现成功,单号:{} 金额:{}", orderId, amount);
      return super.negative(orderId, amount);
  }

  /**
   * 储蓄
   *
   * @param orderId 单号
   * @param amount  金额
   */
  public String recharge(String orderId, BigDecimal amount) {
      // 模拟充值成功
      logger.info("储蓄成功,单号:{} 金额:{}", orderId, amount);
      return super.positive(orderId, amount);
  }

}
信用卡类:
java 复制代码
/**
* @Description: 模拟信用卡功能,重写父类的方法,违法里氏替换原则
* @Author: pupil
* @Date: 2024/06/23 下午 10:32
*/
public class CreditCard extends CashCard{

  private Logger logger = LoggerFactory.getLogger(CashCard.class);

  public CreditCard(String cardNo, String cardDate) {
      super(cardNo, cardDate);
  }

  /**
   * 金额规则
   * 根据里氏替换原则不能重写父类的rule方法,所以新构建一个方法
   * @param amount
   * @return
   */
  boolean rule2(BigDecimal amount) {
      return amount.compareTo(new BigDecimal(1000)) <= 0;
  }


  /**
   * 提现,信用卡贷款
   *
   * @param orderId 单号
   * @param amount  金额
   * @return 状态码 0000成功、0001失败、0002重复
   */
  public String loan(String orderId, BigDecimal amount) {
      boolean rule = rule2(amount);
      if (!rule) {
          logger.info("生成贷款单失败,金额超限。单号:{} 金额:{}", orderId, amount);
          return "0001";
      }
      // 模拟生成贷款单
      logger.info("生成贷款单,单号:{} 金额:{}", orderId, amount);
      // 模拟支付成功
      logger.info("贷款成功,单号:{} 金额:{}", orderId, amount);
      return super.negative(orderId, amount);

  }

  /**
   * 还款,信用卡还款
   *
   * @param orderId 单号
   * @param amount  金额
   * @return 状态码 0000成功、0001失败、0002重复
   */
  public String repayment(String orderId, BigDecimal amount) {
      // 模拟生成还款单
      logger.info("生成还款单,单号:{} 金额:{}", orderId, amount);
      // 模拟还款成功
      logger.info("还款成功,单号:{} 金额:{}", orderId, amount);
      return super.positive(orderId, amount);
  }
}
测试类:
java 复制代码
/**
* @Description: 测试验证
* @Author: pupil
* @Date: 2024/06/23 下午 10:51
*/
public class ApiTest {

  private Logger logger = LoggerFactory.getLogger(ApiTest.class);

  @Test
  public void test_bankCard() {
      logger.info("里氏替换前,CashCard类:");
      CashCard bankCard = new CashCard("800999898", "2024-06-23");
      // 提现
      bankCard.withdrawal("14451", new BigDecimal(100));
      // 储蓄
      bankCard.recharge("14451", new BigDecimal(100));

      logger.info("里氏替换后,CreditCard类:");
      CashCard creditCard = new CreditCard("800999898", "2024-06-23");
      // 提现
      creditCard.withdrawal("14451", new BigDecimal(1000000));
      // 储蓄
      creditCard.recharge("14451", new BigDecimal(100));

      // 交易流水
      List<String> tradeFlow = bankCard.tradeFlow();
      logger.info("查询交易流水,{}", JSON.toJSONString(tradeFlow));
  }

  @Test
  public void test_CreditCard(){
      CreditCard creditCard = new CreditCard("800999898", "2024-06-23");
      // 支付,贷款
      creditCard.loan("14451", new BigDecimal(100));
      // 还款
      creditCard.repayment("14451", new BigDecimal(100));

      // 交易流水
      List<String> tradeFlow = creditCard.tradeFlow();
      logger.info("查询交易流水,{}", JSON.toJSONString(tradeFlow));
  }
}
结果:

根据里氏替换原则的示例类图:

银行卡和储蓄卡是泛化关系,储蓄卡继承于银行卡

储蓄卡和信用卡是泛化关系,信用卡继承于储蓄卡

相关推荐
灰色孤星A7 分钟前
瑞吉外卖项目学习笔记(四)@TableField(fill = FieldFill.INSERT)公共字段填充、启用/禁用/修改员工信息
java·学习笔记·springboot·瑞吉外卖·黑马程序员·tablefield·公共字段填充
逊嘘12 分钟前
【Java数据结构】ArrayList相关的算法
java·开发语言
Y编程小白27 分钟前
SpringBoot的创建方式
java·spring boot·后端
总是学不会.35 分钟前
【集合】Java 8 - Stream API 17种常用操作与案例详解
java·windows·spring boot·mysql·intellij-idea·java集合
潜意识起点44 分钟前
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
java·spring boot·后端
mxbb.1 小时前
单点Redis所面临的问题及解决方法
java·数据库·redis·缓存
云和数据.ChenGuang1 小时前
《XML》教案 第1章 学习XML基础
xml·java·学习
王·小白攻城狮·不是那么帅的哥·天文1 小时前
Java操作Xml
xml·java
发飙的蜗牛'1 小时前
23种设计模式
android·java·设计模式
music0ant2 小时前
Idean 处理一个项目引用另外一个项目jar 但jar版本低的问题
java·pycharm·jar