大家好,我是码农老吴,欢迎收看架构师书房。今天,我继续给大家解读《代码不朽》这本书。
上期,我们聊了本书的第三个原则,不写重复代码(Write Code Once)。知道了代码重复的客观标准,以及1类克隆,2类克隆。那是不是说,从今以后,我们在编写代码时,再也不能使用我们的最爱,ctrl c,ctrl v,杜绝一切重复代码。
江湖里的侠客,千辛万苦或者机缘巧合,获得一本武林秘籍,他往往也不能立即成为武林高手,作者愿意,读者也不愿意,不科学吗,更不用说对咱们理工男,凡事讲科学,讲逻辑。小说的男主,在获得武林秘籍后,往往还需要吃点苦,流点汗,打打怪,刷刷经验,在女主的照顾下,武力值慢慢提升,还不被别人发现。最后,男主在合适的场合,扮猪吃老虎,大*四方,风光无限,最后抱得美人归,归隐江湖,成为武林神话。咱们码农也一样,deadline是第一位的,先编码,再重构,不断提升技巧,刷经验,历经挫折,成为一代技术牛人。
重构之前类图接口及类
CheckingAccount:支票账户
SavingsAccount:储蓄账户
Accounts:账户工具类
Transfer:转账类
CheckingAccount:支票账户makeTransfer()方法,实现支票账户的转账功能,里面有三个步骤,我们重点关注第二步,对账户进行合法性校验。这段代码,将来会发生重复。
package eu.sig.training.ch04.v1;
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!");
}
// 2. Assuming result is 9-digit bank account number, validate 11-test:
int sum = 0;
for (int i = 0; i < counterAccount.length(); i ) {
sum = sum (9-i) * Character.getNumericValue(
counterAccount.charAt(i));
}
if (sum % 11 == 0) {
// 3. Look up counter account and make transfer object:
CheckingAccount acct = Accounts.findAcctByNumber(counterAccount);
Transfer result = new Transfer(this, acct, amount);
return result;
} else {
throw new BusinessException("Invalid account number!");
}
}
}
// end::CheckingAccount[]
SavingsAccount:储蓄账户
这个项目,本来只有一个CheckingAccount:支票账户,随着业务的发展,新增了一类新的账户,储蓄账户,程序员为了简单,直接复制CheckingAccount类的代码,修改之后,就成了现在的样子。
它里面也需要有转账功能,但是转账没有限额,但是也需要对账户进行校验,这段代码在CheckingAccount类里面,已经存在,所以这里发生了重复,需要进行重构。
package eu.sig.training.ch04.v1;
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 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>
// 3. Check whether withdrawal is to registered counter account:
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[]
Accounts:账户工具类
package eu.sig.training.ch04.v1;
public class Accounts {
@SuppressWarnings("unused")
public static CheckingAccount findAcctByNumber(String number) {
return new CheckingAccount();
}
}
Transfer:转账类
package eu.sig.training.ch04.v1;
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:储蓄账户,都有转账功能,转账时都需要进行账户合法性校验,代码发生重复,可以通过前面我们已经分享的重构技巧-提取方法,将重复的代码提取到Accounts类中。
重构之后将上面两个方法中的重复代码,提取到Accounts类中,命名为isValid(),为了方便使用,新提取的方法,常常采用static,也就是静态方法。
Accounts:账户工具类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[]
}
CheckingAccount:支票账户
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[]
SavingsAccount:储蓄账户
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[]
点评
提取方法,可以消除重复的代码,而且往往为了方便快捷,提取为静态方法,这种方式的弊端,就是有可能会造成另外一个极端。形成了包含大量静态方法,方法与方法之间又没有内在联系的大杂烩工具类。从而导致这个工具类,体积膨胀,并且耦合紧密,因为其他大量的类,都依赖这个类。这是另外一种不良现象,如何预防,或者消除新出现的问题,在本书的第6章,分离模块的关注点(Separate Concerns in Modules)这一原则中,将会进行讲解。
关于提取方法,消除重复代码,今天我们就聊到这里,下期我们聊一个新的重构技巧,提取父类,来消除重复代码。
极客架构师,专注架构师成长,我们下期见。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved