当前位置: 首页 > article >正文

重构(4)

(一)添加解释性变量,使得代码更容易理解,更容易调试,也可以方便功能复用

解释性的变量
总价格为商品总价(单价*数量)-折扣(超过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---elder
	int 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---elder
	UserType 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)能够在运行的时候获取的内容,不应该写死。


http://www.kler.cn/a/516253.html

相关文章:

  • Go语言中的值类型和引用类型特点
  • Go学习:常量
  • 服务器安装ESXI7.0系统及通过离线包方式升级到ESXI8.0
  • WordPress Hunk Companion插件节点逻辑缺陷导致Rce漏洞复现(CVE-2024-9707)(附脚本)
  • MySQL可直接使用的查询表的列信息
  • OGG 19C 集成模式启用DDL复制
  • ruoyi-vue-plus 引入 ShardingSphere-JDBC 实现分库分表
  • docker 部署.netcore应用优势在什么地方?
  • Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到
  • 华为OD机试E卷 --树状结构查询--24年OD统一考试(Java JS Python C C++)
  • 概率密度函数(PDF)分布函数(CDF)——直方图累积直方图——直方图规定化的数学基础
  • 智源研究院与乐聚机器人成立具身智能联合实验室
  • 深度学习实战图像OCR识别
  • 【博客之星】2024年度创作成长总结 - 面朝大海 ,春暖花开!
  • STM32——LCD
  • Spring Boot中选择性加载Bean的几种方式
  • 如何使用 Node.js 构建一个简单的 API?
  • Python语言的安全开发
  • 把 PVE 下的机械硬盘(非SSD系统盘)分配给虚拟机使用
  • 打破常规,Linux内核新的数据结构上场maple tree
  • 苍穹外卖—订单模块
  • 由于请求的竞态问题,前端仔喜提了一个bug
  • idea修改模块名导致程序编译出错
  • 【全栈】SprintBoot+vue3迷你商城(3)
  • 谷粒商城——商品服务-三级分类
  • 无界云剪音频教程:提升视频质感