随着对《clean code》的不断深入研读,我越发对自己以前编写的代码感到厌烦,我开始着手去做一些改变,让我不再是一个傻瓜,我想让别人去读懂我的代码,因为我记得这样一句话:“任何傻瓜都能编写计算机看懂的代码,而好的程序员能够编写人看懂的代码”。
短小
前两天,在百度首页上看到这样一张照片,手枪还没有巴掌大,我觉得非常适合Robert的这个主题。
函数是要足够的短小精致。那么具体应该短小到什么程度呢?
- 函数20行封顶最佳。
- 每个函数都依序把你带到下一个函数。
- 函数的缩进层级不该多于一层或者两层。
只做一件事
函数应该只做一件事,并且做好这件事。
判断函数只做一件事的准则就是不能再将该函数再细化。
每个函数一个抽象层级
这个意思就是说,在国家这个层级下面就是省份,省份下面为市级,这样一层层关系明确,类似树形结构。
这样就确保从上向下的一种设计观点,在《人月神话》中也看到这样的观点,自上而下的设计会带来诸多好处,减少bug量的发生等。
switch语句
按照Robert的观点的话,switch语句写起来太过不容易,那么切容我们暂时逃避过去这个小节。
使用描述性的名称
的确好的函数名称,能够让我们很快的了解到函数内部要做的事情,不过这通常很困难。
不要害怕长名称。使用恰如其当的长名称来描述函数内部要做的事情能够让我们一睹为快。那种“犹抱琵琶半遮面”的名称让人费解,也让人难受。
void updateTotalmoneyForTransfermoneyIn(Map usermoney);
曾经我很讨厌这样的长名称,他让代码不易于在一行之内读完,但是这样的名称先让不需要注释就能读懂在干什么(修改总资金为了入金)。
命名方式要一致。如果你有几个功能类似的函数,自然在名称上要保持一致性。
public void sendToQuotationServerStatus() {
public void sendToQuotationJadeQuotations(String scode) {
以上名称就要比下面的好很多,保持步调一致让人舒服多了
public void noticQuotationServerSendQuotationData(String scode) {
public void sendToQuotationServerStatus() {
函数参数
最理想的参数数量是0,其次是1,再次是2,尽量避免3.
我也非常同意Robert的观点:
多参让测试更难执行。
输出参数比输入参数难以理解。
我之前写了这样的函数
private void appendIdToResultAndUpdateMessage(String id, StringBuilder result) {
moneyTransfer.setReturnMessage(MoneyTransfer.MESSAGE_BEFORE_SEND_BANK);
result.append(id);
result.append(",");
}
看着以上的方式和下面的方式,我在犹豫哪一种方式更好?
private StringBuilder getUsefulIdAndUpdateMessage(String id) {
StringBuilder result = new StringBuilder();
moneyTransfer.setReturnMessage(MoneyTransfer.MESSAGE_BEFORE_SEND_BANK);
result.append(id);
result.append(",");
return result;
}
关于标识参数、二元函数、三元函数,我没有找到更好的实践内容?????????????
参数对象。
见Robert给出例子,足以说明问题。
makeCircle(double x, double y, double radius);
makeCircle(Point center, double radius);
很显然,谁都愿意用第二种方式。
参数列表
这个很有意思,见如下代码
System.out.println(String.format("%s月 %s号是 %s的生日", 5, 1, "千千"));// 5月 1号是 千千的生日
无副作用
太多太多时候,我们经常向贪吃蛇一样,咬住了自己的尾巴。如果函数只在某种特殊条件下执行,那么名称最好能标有其环境条件。
public static String getTimeStr(Date date) {
return getDateStr(date, "HH:mm:ss");
}
这样的函数你能吃得消吗?它误导我们可以把日期转换为时间字符串,但是内容却限定了,只能是“HH:mm:ss”,而不能是“HHmmss”,那么最好把该函数重命名。
输出参数
再回头来看看appendIdToResultAndUpdateMessage()这个方法,显然我更应该使用getUsefulIdAndUpdateMessage(),然后这样调用
result.append(getUsefulIdAndUpdateMessage(manyid));
分割指令与询问
函数要么做什么事,要么回答什么事,来看看这样的函数
public void resume() {
if (isAction())
return;
private boolean isAction() {
logger.info("节假日信息:" + ISHOLIDAY + " | " + HASTRADINGJADE);
return ISHOLIDAY || !HASTRADINGJADE;
}
isAction的作用是判断节假日的,如果是节假日,就不再做其他处理,那么这个函数其实不应该有返回值,改造一下
public void resume() {
logger.info("白盘连续竞价恢复...");
checkServerAction();
private void checkServerAction() {
logger.info("节假日信息:" + (ISHOLIDAY ? "是节假日" : "不是节假日") + " | " + (HASTRADINGJADE ? "有商品" : "无商品"));
if (ISHOLIDAY || !HASTRADINGJADE) {
return;
}
}
这样肯定会好一些。
使用异常代替返回错误码
使用错误码的确让人感觉没有使用try catch让人舒服。
try {
message = service.addOrder(orderFromClient, actionType);
} catch (OrderException e) {
logger.warn(e.getMessage());
message = MessageUtils.getFailureMsg(e.getMessage());
本节的其他观点我没有很好的认同感。
别重复自己
这个其实很能体现程序员的水平高低,好的程序员善于把那些重复的代码进行简化做成。
总结:Robert说他自己在一开始写出的函数也曾经冗长,没有人从一开始就能遵守这些规则,那样代码就无法进行下去了。只有在不断的重构优化,才能有更好的代码。
我对这样的自己不感觉到讨厌。
最后附上自己按照作者的观点重构的一部分代码:
public void dailyUpdateSystemData() {
// 每日更新时进行一次会员信息更新
AllMembercoes.init();
checkPrivilege();
// 交易所手动不可操作限制
setSYS_HAND_STATUS(Constants.OTHER_STATUS_END);
updateReloadStatusTrue();
initConfig();
initJadeInfo();
initQuotation();
initUserMoney();
// 刷新一次行情信息
sendToQuotationJadeQuotations();
我觉得突然很欣赏自己。