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

设计模式原则

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

https://gitee.com/Jasonpupil/designPatterns

里氏替换原则

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

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

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

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

使用场景:银行卡存储,

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

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

储蓄卡类:
/**
 * @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;
    }
}
信用卡类:
 /**
   * @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();
      }
  }
测试类:
/**
   * @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));
      }
  }
结果:
  • 在这里插入图片描述
  • 在这里插入图片描述

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

银行卡类:
/**
* @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);
  }

}



信用卡类:
/**
* @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);
  }
}
测试类:
/**
* @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));
  }
}
结果:
  • 在这里插入图片描述

  • 在这里插入图片描述

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

在这里插入图片描述

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

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

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 二、设计模式的六大原则 1、开闭原则(Open Close Principle) 开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 2、里氏代换原则(Liskov Substitution Principle) 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科 3、依赖倒转原则(Dependence Inversion Principle) 这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。 4、接口隔离原则(Interface Segregation Principle) 这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。 5、迪米特法则(最少知道原则)(Demeter Principle) 为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。 6、合成复用原则(Composite Reuse Principle) 原则是尽量使用合成/聚合的方式,而不是使用继承。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值