里氏替换原则

本文通过实例分析了未遵循里氏替换原则导致的问题,并展示了如何通过重构代码来满足这一原则,确保子类可以在父类的基础上扩展功能而不破坏原有逻辑。优化后的设计使得信用卡类在储蓄卡基础上增加功能,同时保持了接口的一致性和系统的稳定性。
摘要由CSDN通过智能技术生成

  如果S是T的子类,那么所以T类型的对象都可以被S类型的对象替换。也就是说子类可以扩展父类的功能但不能改变原有的功能,尽量不重新父类的方法。里氏替换原则包含四点:1、子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。2、子类可以增加自己的方法。3、子类重载父类的方法时,方法的入参要比父类更宽松。4、子类方法重写、重载父类方法或实现抽象方法时,方法的输出或返回应该比父类更严格或相等。

里氏替换原则的作用

1、是实现开闭原则的重要方式。2、解决了重写父类方法导致复用性变差的问题。3、类的扩展不会给原有的系统引入新的错误。4、降低需求变更时引入的风险。

未优化案例

public class CashCard {

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

    /**
     * 提现
     *
     * @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  金额
     */
    public String recharge(String orderId, BigDecimal amount) {
        // 模拟充值成功
        logger.info("储蓄成功,单号:{} 金额:{}", orderId, amount);
        return "0000";
    }

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

}
public class CreditCard extends CashCard {

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

    @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";
    }

    @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();
    }

}

  例子中信用卡类继承了储蓄卡,将提现和储蓄的功能重写为贷款和还款的功能,不满足里氏替换原则。这样写会带来的坏处是不能同时支持两种卡的功能。用里氏替换原则重写的方式是使信用卡在储蓄卡的基础上增加相应的功能。

用里氏替换原则优化案例

public abstract class BankCard {

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

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

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

    abstract boolean rule(BigDecimal amount);

    // 正向入账,+ 钱
    public String positive(String orderId, BigDecimal amount) {
        // 入款成功,存款、还款
        logger.info("卡号{} 入款成功,单号:{} 金额:{}", cardNo, orderId, amount);
        return "0000";
    }

    // 逆向入账,- 钱
    public String negative(String orderId, BigDecimal amount) {
        // 入款成功,存款、还款
        logger.info("卡号{} 出款成功,单号:{} 金额:{}", cardNo, orderId, amount);
        return "0000";
    }

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

    public String getCardNo() {
        return cardNo;
    }

    public String getCardDate() {
        return cardDate;
    }
}
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);
    }

    /**
     * 风险校验
     *
     * @param cardNo  卡号
     * @param orderId 单号
     * @param amount  金额
     * @return 状态
     */
    public boolean checkRisk(String cardNo, String orderId, BigDecimal amount) {
        // 模拟风控校验
        logger.info("风控校验,卡号:{} 单号:{} 金额:{}", cardNo, orderId, amount);
        return true;
    }

}
public class CreditCard extends CashCard {

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

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

    boolean rule2(BigDecimal amount) {
        return amount.compareTo(new BigDecimal(1000)) <= 0;
    }

    /**
     * 提现,信用卡贷款
     *
     * @param orderId 单号
     * @param amount  金额
     * @return 状态码
     */
    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 状态码
     */
    public String repayment(String orderId, BigDecimal amount) {
        // 模拟生成还款单
        logger.info("生成还款单,单号:{} 金额:{}", orderId, amount);
        // 模拟还款成功
        logger.info("还款成功,单号:{} 金额:{}", orderId, amount);
        return super.positive(orderId, amount);
    }

}

  优化的案例中,先抽象出一个银行卡抽象类,提供公共的出账入账方法,然后储蓄卡实现了规则过滤方法,新增了提现、储蓄、风控校验的功能,接着信用卡继承储蓄卡,在储蓄卡的基础上新增贷款还款的功能,这样对原有的逻辑不产生影响。在使用过程中子类可以替换父类,也就是满足了里氏替换原则。


参考资料:《重新Java设计模式》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值