对于软件开发管理来说,制订编码规范是一个历久弥新的话题。每一个大有为开发头目,都强调编码规范。或者程序员也互相攻讦(jie,第二声,阳平),说你写的代码很乱,不规范,很难看懂,很难维护。总之别人写的都不行,自己写的才是好的。
那么,怎么才算编码规范?按照我的经历,大家说编码规范,一般都是聚焦于命名规则,注释。命名现在可能普遍没啥问题了,驼峰命名,英文(而不是拼音缩写);注释,刚开始就一本正经地写上作者,创建时间,更新时间。其实时间一长,经手的人多了,就形同虚设,鸡肋得很。其实,在哪里注释、注释针对对象,注释内容比注释格式要重要得多。
下面我总结一下我自己认为的,编码规范需要注意的地方。
一、注释
1、要有个代码文件说明
项目级有个项目说明;如果项目内有多个子系统,每个子系统也要有个子系统说明。这不是写在代码里面,而是放在项目外面,严格来说不算注释,但这个说明真的很重要。如果是交接工作,后来者接手,或者隔了很长时间以后,自己看到,可以大致知道这份代码是做什么的。
2、代码文件注释
在文件头部,简要说明本文件的作用。
3、重要方法注释
对于一些重要的方法,说明一下
4、重要或难懂的处理逻辑的注释
5、小结
注释不是越多越好,太多容易分神。如果注释跟代码不同步,比如复制粘贴,或者代码改了,注释没改,甚至起反作用。只有重要、难懂的地方才需要写注释。
二、条件语句中的代码行数
每个条件判断,处理的代码行数非常多,需要不停地上下滚屏,甚至滚屏滚到手酸,才能看到 else 或结束符。然后想看看开头吧,好于是又一轮滚屏开始了。这样很难从总体上对这个条件判断进行理解。我今年接手的项目,很多这样的情况,简直是噩梦。一时找不到这些代码了,下面这个例子是AI给的,不算最夸张,只是嵌套多一点而已。
public void processUser(User user) {
if (user != null) {
if (user.getRole() != null && user.getRole().equals("ADMIN")) {
if (user.getAge() > 18) {
if (user.getStatus() != null && user.getStatus().equals("ACTIVE")) {
// 处理活跃管理员逻辑,包含大量冗长业务逻辑
log("Processing admin user...");
performAdminTask1(user);
performAdminTask2(user);
performAdminTask3(user);
// 大量的日志记录和数据处理逻辑
for (int i = 0; i < 100; i++) {
log("Processing batch task: " + i);
performBatchOperation(user, i);
}
// 更加夸张的文件处理操作
for (File file : getAdminFiles(user)) {
try {
readAndProcessFile(file);
log("File processed: " + file.getName());
updateFileStatus(file);
archiveProcessedFile(file);
validateFileIntegrity(file);
synchronizeFileWithDatabase(file);
} catch (Exception e) {
log("Error processing file: " + file.getName() + " - " + e.getMessage());
}
}
// 各种同步任务
for (int i = 0; i < 50; i++) {
syncTaskWithServer(user, i);
log("Sync task with server " + i + " completed.");
}
// 模拟数据同步和备份逻辑
backupAdminData(user);
syncAdminDataAcrossServers(user);
log("Admin data backup and sync completed.");
// 大量的系统调用和日志记录
for (int i = 0; i < 200; i++) {
systemCallOperation(user, i);
log("System call " + i + " executed.");
}
// 结束逻辑
updateLoginRecord(user);
log("Finished processing admin user: " + user.getUsername());
} else {
// 处理非活跃管理员
log("Admin user is not active. Running inactive admin process...");
performInactiveAdminTask1(user);
performInactiveAdminTask2(user);
sendNotification(user, "Your admin account is not active.");
// 更加夸张的日志和数据库清理操作
for (int i = 0; i < 1000; i++) {
log("Cleaning up inactive admin record " + i);
performDatabaseCleanup(user, i);
}
// 文件操作和数据恢复
for (File file : getInactiveAdminFiles(user)) {
restoreFileFromBackup(file);
validateFileAfterRestore(file);
log("Restored and validated file: " + file.getName());
}
log("Inactive admin cleanup and restoration complete for user: " + user.getUsername());
}
} else {
// 处理未成年人管理员
log("Admin user is underage. Starting verification process...");
sendVerificationEmail(user);
log("Verification email sent.");
// 冗长的家庭联系操作
for (int i = 0; i < 10; i++) {
updateParentContact(user, i);
log("Updated parental contact for underage admin: " + i);
generateParentalConsentDocument(user, i);
sendParentalConsentRequest(user);
log("Parental consent request sent for attempt: " + i);
}
// 模拟更多的业务逻辑
for (int i = 0; i < 50; i++) {
performUnderageAdminTask(user, i);
log("Underage admin task " + i + " completed.");
}
// 最后的日志记录
log("Underage admin process complete for user: " + user.getUsername());
}
} else if (user.getRole() != null && user.getRole().equals("USER")) {
if (user.getAccountStatus() != null && user.getAccountStatus().equals("ACTIVE")) {
// 处理普通用户,添加更多的冗长处理逻辑
log("Processing active user...");
sendDiscountOffer(user);
sendPromotionalEmails(user);
// 模拟复杂的业务操作
for (int i = 0; i < 500; i++) {
performUserActivityLogging(user, i);
performUserPreferencesUpdate(user, i);
generateUserReport(user, i);
log("Processed user activity for batch: " + i);
}
// 文件操作
for (File file : getUserActivityFiles(user)) {
processUserActivityFile(file);
log("Processed activity file: " + file.getName());
archiveUserFile(file);
synchronizeUserFileWithCloud(file);
validateUserFile(file);
}
log("Finished processing active user: " + user.getUsername());
} else {
// 处理非活动用户
log("Inactive user detected. Processing account cleanup...");
for (int i = 0; i < 300; i++) {
performAccountDeactivation(user, i);
log("Deactivated user account batch: " + i);
}
// 数据删除操作
for (int i = 0; i < 100; i++) {
deleteUserData(user, i);
log("User data batch " + i + " deleted.");
}
log("Inactive user cleanup complete for user: " + user.getUsername());
}
} else {
// 未知角色用户的处理
log("Unrecognized user role. Handling unknown user...");
for (int i = 0; i < 50; i++) {
performUnknownRoleTask(user, i);
log("Processed unknown role task " + i);
}
// 结束未知角色处理
log("Finished processing user with unrecognized role: " + user.getUsername());
}
} else {
// 用户为空的处理逻辑
log("User object is null. Skipping processing...");
for (int i = 0; i < 10; i++) {
handleNullUserRecord(i);
log("Handled null user record " + i);
}
// 日志记录
log("Null user handling completed.");
}
}
对应这种情况,可以使用函数,将每个条件判断的处理逻辑,放到函数里。一般我们写一个函数,最大的作用是复用;但为了可读性,就算不需要复用,也可以写成函数。比如我将之前的一些代码改写成这样:
这样利于从整体上观察处理逻辑。
三、避免硬编码
硬编码就是将一些写死到代码里,后期很难修改。常见的如:
1、将地址写到代码里
解决之道是将地址写到配置文件里
2、使用数字作为条件判断的依据
比如 if(type === 1) 之类,大家一定不会陌生,可读性很差,尤其是对接手的人,或者时间久远的项目。解决办法众所周知,后台可以用枚举,前端也可以定义常量来代替。如
// 定义常量
const MAX_LOGIN_ATTEMPTS = 5;
const USER_ROLE_ADMIN = 1;
const USER_ROLE_EDITOR = 2;
const USER_ROLE_VIEWER = 3;
const PASSWORD_MIN_LENGTH = 8;
// 函数用于检查用户登录尝试
function checkLoginAttempts(attempts) {
if (attempts > MAX_LOGIN_ATTEMPTS) {
console.log("Too many login attempts. Please try again later.");
} else {
console.log("Login attempt successful.");
}
}
// 函数用于用户权限检查
function checkUserRole(role) {
switch (role) {
case USER_ROLE_ADMIN:
console.log("User has admin privileges.");
break;
case USER_ROLE_EDITOR:
console.log("User has editor privileges.");
break;
case USER_ROLE_VIEWER:
console.log("User has viewer privileges.");
break;
default:
console.log("Unknown user role.");
break;
}
}
// 函数用于验证密码长度
function validatePassword(password) {
if (password.length < PASSWORD_MIN_LENGTH) {
console.log(`Password must be at least ${PASSWORD_MIN_LENGTH} characters long.`);
} else {
console.log("Password is valid.");
}
}
// 示例调用
checkLoginAttempts(6);
checkUserRole(2);
validatePassword("short");