六大设计原则
单一职责原则定义:约定一个类应该有且仅有一个改变类的原因;
开闭原则定义:规定软件中的对象、类、模块和函数对扩展应该是开放的,但对于修改是封闭的,核心思想也可以理解为面向抽象编程。
里氏替换原则定义:继承必须确保超类所拥有的性质在子类中仍然成立。
里氏替换原则概述:如果S是T的子类型,那么所有T类型的对象都可以在不破坏程序的情况下被S类型的对象替换。简单来说,子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:当子类继承父类时,除添加新的方法且完成新增功能外,尽量不要重写父类的方法。这句话包括了四点含义:·
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法;
- 子类可以增加自己特有的方法;
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松;
- 当子类的方法实现父类的方法(重写、重载或实现抽象方法)时,方法的后置条件(即方法的输出或返回值)要比父类的方法更严格或与父类的方法相等。
里氏替换原则的作用:
- 里氏替换原则是实现开闭原则的重要方式之一;
- 解决了继承中重写父类造成的可复用性变差的问题;
- 是动作正确性的保证,即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性;
- 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。
示例
假设在构建银行系统时,储蓄卡是第一个类,信用卡是第二个类。为了让信用卡可以使用储蓄卡的一些方法,选择由信用卡类继承储蓄卡类,讨论是否满足里氏替换原则产生的一些要点。
违背原则方案
储蓄卡和信用卡在使用功能上类似,都有支付、提现、还款、充值等功能,也有些许不同,例如支付,储蓄卡做的是账户扣款动作,信用卡做的是生成贷款单动作。下面这里模拟先有储蓄卡的类,之后继承这个类的基本功能,以实现信用卡的功能。
储蓄卡
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);