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

设计模式原则

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

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));
  }
}
结果:

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

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

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

相关推荐
阿伟*rui40 分钟前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
XiaoLeisj3 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck3 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei3 小时前
java的类加载机制的学习
java·学习
Yaml45 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~5 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616885 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7895 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java6 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
睡觉谁叫~~~6 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust