(一)添加解释性变量,使得代码更容易理解,更容易调试,也可以方便功能复用
解释性的变量
总价格为商品总价(单价*数量)-折扣(超过100个以上的打9折)+邮费(原价的10%,50元封顶)double getTotalPrice()
{return price * quantity - max(0, quantity - 100) * price - min(quantity * price * 0.1, 50);
}double getTotalPrice()
{double baseTotalPrice = price * quantity;double discountPrice = max(0, quantity - 100) * price;double postPrice = min(quantity * price * 0.1, 50);return baseTotalPrice - discountPrice + postPrice;
}
(二)函数中bool类型的参数的优化
就是把bool给剥离出来,构成单一职责
1.原始未优化
double calcFinalAmount(double originAmount, bool isChild)
{if(isChild){return originAmount * 0.5;}else{return originAmount;}
}
2.代码简单优化
//职责不单一
double calcFinalAmount(double originAmount, bool isChild)
{double discount = isChild ? 0.5 : 1;return originAmount * discount;
}
3.根源变量单一职责
//单一职责,根源可变变量划分
double getDiscount(bool isChild)
{return isChild ? 0.5 : 1;
}
double calcFinalAmount(double originAmount, bool isChild)
{double discount = getDiscount(isChild);return originAmount * discount;
}
4.功能单一职责
//单一职责,功能划分
double calcFinalAmountForChild(double originAmount, bool isChild)
{return originAmount * 0.5;
}
double calcFinalAmountForAdult(double originAmount, bool isChild)
{return originAmount;
}
(三)令人抓狂的字符串组合
硬拼接---->变成格式化拼接
//硬拼接
std::string userInfo = "user info:" + "id: " + userId.to_string() + "Name: " + user.Name());
//格式化拼接
char data[200];
sprintf(data, "user info : id : %s, Name : %s", userId.to_string(), user.Name());
(四)函数复用性优化
struct Users
{int status;
};
// 函数复用性很差
Users queryUserStatusIs10();
Users queryUserStatusIs5();
// 改善复用性之后
Users queryUsersByStatus(int status);
(五)避免魔法数字
1.未优化
std::string getUserStrType()
{// 1--- child 2---adult 3---elderint userType = 0;if (userType == 0){return "child";}else if (userType == 1){return "adult";}else if (userType == 2){return "elder";}
}
优化:
enum UserType
{CHILD,ADULT,ELDER,
};std::string getUserStrType()
{// 1--- child 2---adult 3---elderUserType ut = UserType::CHILD;if (ut == UserType::CHILD){return "child";}else if (ut == UserType::ADULT){return "adult";}else if (ut == UserType::ELDER){return "elder";}
}
(六)长函数分解优化(注释驱动原则)
每当刚觉要用注释来说明解释用途的时候,这时候就需要把它抽离出来做成一个单独的函数。简单来说就是要进行功能划分,满足单一职责原则。
优化之前:
void calcMoney()
{//1. 计算总金额,如果是vip,则打95折(代码块)//2. 根据金额生成付款码{代码块)//3. 初始化付款任务{代码块)
}
优化后:
double calcSumMoney();
double generateRandomCoded(double sumPrice);
bool initTask();void calcMoney()
{//1. 计算总金额,如果是vip,则打95折(函数)double sumPrice = calcSumMoney();//2. 根据金额生成付款码{代码块)generateRandomCoded(sumPrice);//3. 初始化付款任务initTask();
}
(七)复用函数的提炼
(八)多参数函数的优化
把参数封装到结构体中。参数有可能传入错误,顺序错误,采用结构体的方法可以避免这种问题,总结来说,当参数大于等于4个,就可以采用这种方式。
1.未优化
int queryResultFromServer(strName, queryId, userID, userRegion, bIsNewVersion);
2.优化之后
可以把前4个参数,放到一个对象里面,比如结构体里面。
struct Obj
{std::string strName;int queryId;int userID;int userRegion;bool bIsNewVersion;
};
int queryResultFromServer(Obj obj);
(九)查询代替临时变量
优化前:
double getPrice()
{double basePrice = quantity * itemPrice;double discount = 1.0;if (basePrice > 1000){discount = 0.95;}return basePrice * discount;
}
优化后:
//分解为
double getBasePrice()
{return quantity* itemPrice;
}
double getDiscount()
{return getBasePrice() > 1000 ? 0.95 :0.98;
}
double getPrice()
{return getBasePrice() * getDiscount();
}
(十)数组的错误使用(属性字段)
数字改成结构体或者类,并且字段用函数来查询
class Person
{
public:Person(int age, std::string name, std::string country){m_age = age;m_name = name;m_country = country;}int getAge(){return m_age;;}std::string getName(){return m_name;}std::string getCountry(){return m_country;}private:int m_age;std::string m_name;std::string m_country;
};void printInfo(Person& person)
{printf("name: %s, age: %d, country: %s", person.getName(), person.getAge(), person.getCountry());
}
(十一)函数的副作用
1.修改任何外部的变量、对象属性和数据结构;
2.控制台输入和输出交互;
3.文件操作和网络操作
4.抛出异常或者错误终止;
//错误写法
void getMaxScore()
{arrScore = arrScore.sort();return arrScore[0];
}
传入同一个参数,得到的结果不同,这就是非纯函数,一般不能用的。
(十二) 判断函数的命名
1) is/can/could/should/need +[名词】 + 形容词,比如 isUserNotExists(); isUserIdAndAgeValid();
2) isUserValid,正逻辑, isUserNotValid,就是反逻辑。尽量使用正逻辑;
3)isUserNameAndIdValid();应该改拆成 isUserAgeValid(); isUserNameValid();,功能单一,使用起来易于扩展
(十三)硬编码,变量写死
1)把硬编码改成 常量+ 变量的字符串拼接。
2)定义成全局常量
(十四)变量命名
1)int days 写成 int elapsedTimesInDays; 什么的天;
2) 命名次数,times, 改成 timesOfRequestRetry; 究竟是对于什么的次数。
3)flag,,,改成isProcessFinished;
4)createdFiles;
(十五)区分命名
1)数字命名
//数字命名,
void copyChars(char a1[], char a2[]);
//改成
void copyChars(char src[], char dst[]);
2)无意义的命名
class Product; class ProductInfo; class ProductData;
3)strName, 这时候添加类型名是多余的;
bIsAgeValid中的b就是多余的。
如果变量中类型名是多余的,那就不要类型名。
(十六)避免误导
1)缩写误导
Rgb2Gray(0正确,
setDataSta(), 这个sta就难以理解了,不是一个通用的简写方法。
queryUserAdd(); add作者想表达是地址,但是现在用add会给人带来误导。
用简写就用通用的简写,大众不认可的不要用,不认可的情况下就用全写。
2)多义词误导
bool setMonitorTime(); 设置监视持续多少时间? 开始时间? 监控次数?等等;
queryRegistryContent(); register是注册表吗?
3)变量类型误导
class Account;
Account[] accountList, 这时候大家可能认为它是list的类型,容易搞混淆。
4)外形相似误导
xyzControllerForHnadlingOfString() 和 xyzControllerForStorageOfString();
这样不仔细看,还以为是同一个函数。
0和o比较相似; l和i和1也比较相似;
(十七)函数中动词选用指南
1)避免滥用通用词
getTotalAmount(); get此滥用。
addCharacter(); 添加一个字符,困惑,添加到头部还是尾部?
一些词汇,创建(create,init,load),销毁(destroy,release,uninit,deinit),动词(get,fetch,load,read,write,find、serach,receive,pull),(set, write,put,push),更新(update,reset,refresh),添加/移除(add,remove,append,insert,delete);启动/停止(start,open,launch, close,stop,finish);
(十八)函数命名动词选取
filter,mergeBy,contact, split, deduplicate, reverse, sort,fill,parse, analysis, format,convert,ensure,
(十九)代码review
1)注意
无意义的注释,过多篇幅的注释,修改代码不更新注释导致无法理解;清晰的代码是不需要过多的注释说明的,
基础的功能应该封装成代码。
函数过长,功能应该被划分。
重复代码不做复用。
按照职责设计和提炼函数,尽量做到函数的单一职责。
相同逻辑的代码积极复用。
2)典型问题
定义的时候不考虑解耦;
使用常量代替应该动态使用的内容。
3)能够在运行的时候获取的内容,不应该写死。