大家好,我是码农老吴,欢迎收看架构师书房。今天,我继续给大家解读《代码不朽》这本书。
上期,我们聊完了如何通过提取静态方法,消除重复代码。本期,我们聊聊消除重复代码的第二个方法,提取父类,也就是通过面对对象里面的继承,来消除重复代码。儿子欠了技术债,父亲来还债,子债父还吗。还是上次的案例。
接口及类
CheckingAccount:支票账户
SavingsAccount:储蓄账户
Accounts:账户工具类
Transfer:转账类
package eu.sig.training.ch04.v2;
public class Accounts {
@SuppressWarnings("unused")
public static CheckingAccount findAcctByNumber(String number) {
return new CheckingAccount();
}
// tag::isValid[]
public static boolean isValid(String number) {
int sum = 0;
for (int i = 0; i < number.length(); i++) {
sum = sum + (9 - i) * Character.getNumericValue(number.charAt(i));
}
return sum % 11 == 0;
}
// end::isValid[]
}package eu.sig.training.ch04.v2;
import eu.sig.training.ch04.BusinessException;
import eu.sig.training.ch04.Money;
// tag::CheckingAccount[]
public class CheckingAccount {
private int transferLimit = 100;
public Transfer makeTransfer(String counterAccount, Money amount)
throws BusinessException {
// 1. Check withdrawal limit:
if (amount.greaterThan(this.transferLimit)) {
throw new BusinessException("Limit exceeded!");
}
if (Accounts.isValid(counterAccount)) { // <1>
// 2. Look up counter account and make transfer object:
CheckingAccount acct = Accounts.findAcctByNumber(counterAccount);
Transfer result = new Transfer(this, acct, amount); // <2>
return result;
} else {
throw new BusinessException("Invalid account number!");
}
}
}
// end::CheckingAccount[]package eu.sig.training.ch04.v2;
import eu.sig.training.ch04.BusinessException;
import eu.sig.training.ch04.Money;
// tag::SavingsAccount[]
public class SavingsAccount {
CheckingAccount registeredCounterAccount;
public Transfer makeTransfer(String counterAccount, Money amount)
throws BusinessException {
// 1. Assuming result is 9-digit bank account number,
// validate with 11-test:
if (Accounts.isValid(counterAccount)) { // <1>
// 2. Look up counter account and make transfer object:
CheckingAccount acct = Accounts.findAcctByNumber(counterAccount);
Transfer result = new Transfer(this, acct, amount); // <2>
if (result.getCounterAccount().equals(this.registeredCounterAccount))
{
return result;
} else {
throw new BusinessException("Counter-account not registered!");
}
} else {
throw new BusinessException("Invalid account number!!");
}
}
}
// end::SavingsAccount[]package eu.sig.training.ch04.v2;
import eu.sig.training.ch04.Money;
public class Transfer {
CheckingAccount counterAccount;
@SuppressWarnings("unused")
public Transfer(CheckingAccount acct1, CheckingAccount acct2, Money m) {}
@SuppressWarnings("unused")
public Transfer(SavingsAccount acct1, CheckingAccount acct2, Money m) {}
public CheckingAccount getCounterAccount() {
return this.counterAccount;
}
}CheckingAccount:支票账户和SavingsAccount:储蓄账户,从这两个类的名字上,就可以看出,他们的相似性,从面向对象的角度看,这两个类,应该都属于账户。就可以建立一个新的类,账户类,然后,将两个子类中,重复的代码,提取到父类中,基于继承实现代码复用。
接口及类
Account:账户父类
CheckingAccount:支票账户
SavingsAccount:储蓄账户
父类的makeTransfer方法,提供了一种默认的转账操作,子类中,如果该方法,没有变化,则可以直接复用,反之,覆盖父类的方法即可。
package eu.sig.training.ch04.v3;
import eu.sig.training.ch04.BusinessException;
import eu.sig.training.ch04.Money;
// tag::Account[]
public class Account {
public Transfer makeTransfer(String counterAccount, Money amount)
throws BusinessException {
// 1. Assuming result is 9-digit bank account number, validate 11-test:
int sum = 0; // <1>
for (int i = 0; i < counterAccount.length(); i++) {
sum = sum + (9 - i) * Character.
getNumericValue(counterAccount.charAt(i));
}
if (sum % 11 == 0) {
// 2. Look up counter account and make transfer object:
CheckingAccount acct = Accounts.findAcctByNumber(counterAccount);
Transfer result = new Transfer(this, acct, amount); // <2>
return result;
} else {
throw new BusinessException("Invalid account number!");
}
}
}
// end::Account[]子类makeTransfer方法,既有自己的业务逻辑,又通过super关键字调用了父类提供的makeTransfer();
package eu.sig.training.ch04.v3;
import eu.sig.training.ch04.BusinessException;
import eu.sig.training.ch04.Money;
// tag::CheckingAccount[]
public class CheckingAccount extends Account {
private int transferLimit = 100;
@Override
public Transfer makeTransfer(String counterAccount, Money amount)
throws BusinessException {
if (amount.greaterThan(this.transferLimit)) {
throw new BusinessException("Limit exceeded!");
}
return super.makeTransfer(counterAccount, amount);
}
}
// end::CheckingAccount[]同上
package eu.sig.training.ch04.v3;
import eu.sig.training.ch04.BusinessException;
import eu.sig.training.ch04.Money;
// tag::SavingsAccount[]
public class SavingsAccount extends Account {
CheckingAccount registeredCounterAccount;
@Override
public Transfer makeTransfer(String counterAccount, Money amount)
throws BusinessException {
Transfer result = super.makeTransfer(counterAccount, amount);
if (result.getCounterAccount().equals(this.registeredCounterAccount)) {
return result;
} else {
throw new BusinessException("Counter-account not registered!");
}
}
}
// end::SavingsAccount[]提取父类,作为一种重构技巧,无可厚非。但是,根据我多年的工作经验,从多个相似的类中,提取它们的父类,发生的概率一般不高,原因如下。
1,先建立接口和父类,然后增加子类:这是比较常见的情况,不然要架构师干啥呢,连系统中哪里需要建立父类,都识别不出来,能力堪忧啊。
2,先有一个类(将来会进化为子类),然后在系统扩展时,增加第二个相似的类之前,提取父类,否则说明程序员对项目一点都不了解。也就是意味着,在增加第二个子类之前,父类就应该设计出来。
关于第三个原则,不写重复代码(Write Code Once),通过提取静态方法和提取父类,消除重复代码,我们就聊到这里,下期,我们开始聊第四个原则,保持代码单元的接口简单(Keep Unit Interfaces Small)。
极客架构师,专注架构师成长,我们下期见。
| 留言与评论(共有 0 条评论) “” |