尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从命名规范到函数设计:干净代码的四大核心习惯与实战指南

从命名规范到函数设计:干净代码的四大核心习惯与实战指南 1. 从“能跑就行”到“干净代码”一个普通开发者的觉醒我见过太多代码库也写过不少。早期我和很多人一样信奉“功能第一能跑就行”。一个函数写几百行变量名用a、b、c注释要么没有要么是几个月后自己都看不懂的“这里要改”。直到有一次我接手维护一个离职同事的项目那感觉就像走进了一个堆满杂物的仓库没有灯地上全是绊脚绳。一个简单的需求变更我花了整整一周才理清头绪期间还因为误改了某个“神秘”的全局变量引发了线上事故。那次经历让我痛定思痛代码首先是写给人看的其次才是给机器执行的。“干净代码”这个词听起来很高大上似乎和“设计模式”、“领域驱动设计”、“六边形架构”这些概念绑在一起让人望而却步。很多人觉得那是架构师或者高级专家才需要考虑的事情我们这些写业务逻辑的“码农”能把需求实现就不错了哪有时间搞这些“形式主义”但我想说这是一个巨大的误解。干净代码不是一套复杂的理论体系而是一系列可以立刻上手、成本极低的编程习惯。它不要求你精通什么高深架构它的核心目标极其朴素让你和你的同事在三个月甚至三年后还能快速理解、修改和扩展这段代码。看看那些热搜词吧“命名规范”、“字段注释”、“函数”、“结构”、“修改结构”、“命名冲突”、“注释快捷键”……这几乎勾勒出了一个普通开发者日常编码时遇到的所有痛点。我们不是在讨论玄学而是在解决这些实实在在的、让你加班、让你头疼的问题。一套能落地的干净代码习惯就是帮你把这些散落的“坑”填平铺成一条平坦的路。本文的目的就是抛开那些晦涩的理论直接给你一套从命名、函数、注释到结构四个维度立即可用、用了就有效的“干净代码”实操指南。无论你是刚入行的新手还是被烂代码折磨已久的老兵这些习惯都能让你的编码体验和产出质量发生肉眼可见的提升。2. 命名的艺术让代码自己说话命名是代码中最常被阅读的部分也是最容易被忽视的部分。一个好的名字胜过十行注释。我们经常在热搜里看到“命名规范”、“避免命名冲突”这恰恰说明了命名混乱是普遍痛点。2.1 命名的核心原则意图清晰无歧义命名的最高境界是“见名知意”。变量、函数、类的名字应该明确地告诉你它“是什么”以及“为什么存在”。坏例子data,process,info,temp,flag。这些名字除了占用空间没有传递任何有效信息。flag是标志什么process是处理什么data又是什么数据好例子参考热搜词“颜色控制滑块”的案例。skinhueslider,skinsatslider,skinbrightslider。虽然这个命名还有优化空间比如用SkinHueSlider更符合驼峰命名法但它清晰地传达了这是控制皮肤skin色调hue的滑块slider。任何一个开发者看到这个名字都能立刻理解其用途无需查看其实现或寻找注释。命名的长度不是问题清晰度才是关键。不要害怕使用长名字现代IDE的自动补全功能完全能handle。customerOrderList远比list1要好得多。2.2 函数与类命名用动词和名词说清职责函数名应该是一个动词或动宾短语清晰地表达这个函数“做什么”。坏例子getData()。获取什么数据从哪里获取好例子calculateOrderTotal(),fetchUserProfileFromAPI(),validateEmailFormat()。函数名就说明了动作计算、获取、验证和对象订单总额、用户资料、邮箱格式。类名应该是一个名词或名词短语表示它“是什么”。坏例子Manager,Processor,Utils。这些名字太泛职责模糊。一个OrderManager可能既处理创建又处理支付还处理发货最终变成一个上帝类。好例子Order,CustomerRepository,EmailValidator,PaymentGatewayClient。类名清晰地界定了它的边界和核心职责。2.3 避免误导和歧义这是命名中最危险的陷阱。比如一个名为accountList的变量如果它实际上不是一个List类型而是一个Array或Set就会对阅读者产生严重的误导。应该命名为accounts或accountGroup。同样热搜中提到的“mutation中使用state怎么避免命名冲突”在Vuex或类似状态管理中一个常见的技巧是使用命名空间namespaces来隔离不同模块的state、getters、mutations和actions或者在使用时通过解构重命名来避免冲突例如import { state as userState } from ‘./modules/user‘。注意不要使用小写字母l和大写字母O作为变量名因为它们极易与数字1和0混淆。这是一条铁律。2.4 一套可落地的命名检查清单在你写完一个名字后可以快速问自己几个问题它是否描述了“什么”和“为什么”而不仅仅是“如何”如何实现是代码的事。如果我把它拿给一个不熟悉这段代码的同事看他能猜出它是干什么的吗这个名字有没有和我项目里其他名字冲突或相似得容易混淆例如startTime和startedAt哪个更好通常At后缀表示时间点For后缀表示时长如durationForProcessing。它是否遵循了项目/团队约定的命名规范如驼峰camelCase、帕斯卡PascalCase、蛇形snake_case。养成这个自我提问的习惯你的代码可读性会立刻提升一个档次。3. 函数的打磨短小精悍只做一件事函数是构建程序的基石也是最容易变得臃肿混乱的地方。干净代码要求函数应该像一篇篇短文每个函数只讲述一个故事。3.1 第一原则短小再短小20行是个不错的心理上限10行以内更佳。长的函数意味着复杂的逻辑嵌套难以理解、测试和维护。当你发现一个函数超过一屏约40-50行时就应该高度警惕思考是否可以拆解。如何拆解寻找代码块中的抽象层级。一个处理订单的函数内部可能依次有“验证订单”、“计算价格”、“扣减库存”、“生成日志”等步骤。这些步骤就是天然的抽象点每个步骤都应该被提取成一个独立的、名字清晰的函数。于是你的主函数就会变得像一份清晰的“执行清单”def process_order(order_data): validate_order(order_data) total_price calculate_order_price(order_data) reduce_inventory(order_data) create_order_log(order_data, total_price) return total_price这样阅读process_order函数的人无需深入细节就能立刻把握整个订单处理的流程。如果想了解某个步骤的细节再进入对应的子函数查看。3.2 第二原则单一职责一个函数只做一件事这是“短小”原则的内在要求。一个函数应该只完成一个逻辑上的任务。如何判断它是否只做了一件事看它是否能再拆出一个不是单纯重新表述其实现细节的函数。例如一个名为save_user_and_send_email的函数显然做了“保存用户”和“发送邮件”两件事。它应该被拆分成save_user()和send_welcome_email(user)两个函数。这不仅更清晰也提高了可复用性可能其他地方只需要保存用户而不需要发邮件。3.3 函数参数越少越好函数的参数数量直接影响其复杂度和可测试性。理想情况下参数应控制在0-2个3个尚可接受超过3个就需要慎重考虑。零参数无参函数最理想通常对应查询或执行一个非常具体的命令。单参数常见于转换或验证操作如format_date(timestamp)。双参数常见于二元操作如calculate_distance(point_a, point_b)。多参数问题参数过多时调用者容易搞错顺序阅读时也难以理解。此时应考虑封装成对象如果多个参数总是同时出现来描述一个事物比如创建用户时需要name,email,age那么应该定义一个User类或结构体将参数封装为create_user(User user)。拆分函数可能这个函数做了太多事需要被拆分。使用Builder模式或命名参数如果语言支持在一些语言中可以使用具名参数来避免顺序错误提高可读性。3.4 无副作用与命令查询分离这是函数设计的高级习惯但理解后收益巨大。无副作用函数应该像数学中的函数给定相同的输入永远返回相同的输出并且不修改任何外部状态如全局变量、传入的引用参数。这使函数极度可预测、可测试。热搜中的“虚函数”是C等多态机制的概念其设计也应遵循这一原则确保行为可预期。命令查询分离一个函数要么是“命令”执行一个动作修改状态返回void或操作结果标识要么是“查询”返回数据但不修改任何状态。千万不要混用。例如get_user_and_update_last_login()就是一个糟糕的设计它把查询和命令耦合在了一起。应该拆成user get_user(id)和update_last_login(id)。遵循这些函数原则你的代码块会变得像乐高积木一样清晰、独立、易于组合和替换。4. 注释的正确姿势解释“为什么”而非“是什么”注释是必要的恶。好的注释弥足珍贵坏的注释包括过时的注释比没有注释更糟糕因为它会提供错误的信息。热搜里“字段注释”、“包注释”、“idea新建类默认注释”等词的热度反映了大家对注释的重视和困惑。4.1 什么情况下需要写注释核心原则代码应该自解释注释是用来解释代码无法表达的信息尤其是“为什么”要这么做。需要写注释的情况解释意图Why当代码背后的业务逻辑或设计决策不那么明显时。例如// 使用快速排序而非归并排序因为此处数据基本有序快速排序平均性能更好。 quickSort(data);警示后果Warning当某些代码有非显而易见的副作用或风险时。# 警告此函数会直接修改传入的原始列表调用前请确认。 def normalize_list(lst): ...TODO/FIXME标记标明临时代码、已知缺陷或待完成的功能。这是与未来自己或同事的约定。// TODO: 2023-10-27 此处需要优化当数据量超过1w时性能下降明显。 function processLargeData(data) { ... }公共API文档对于暴露给其他模块或开发者使用的类、函数、接口必须提供清晰的文档注释如Javadoc, Pydoc说明其用途、参数、返回值和可能抛出的异常。法律信息或版权声明。不需要写注释的情况因为代码本身已说明冗余注释注释只是重复代码字面意思。i; // i加1日志式注释在代码中记录修改历史。这应该由版本控制系统如Git来管理。// 修改人张三 日期2023-01-01 修改了XX逻辑括号后的注释用于标记代码块结束。这通常意味着你的函数或代码块太长了需要拆分。} // end of if4.2 如何写好注释简洁准确用最精炼的语言表达完整的意思。使用正确的语法和拼写错误的注释会显得很不专业。保持更新最危险的注释是过时的注释。当代码修改时必须检查并更新相关的注释。如果做不到宁可不写。利用IDE工具像热搜中提到的“idea配置快速生产类注释的快捷键”这是非常好的实践。配置统一的、包含作者、日期、描述等信息的类/方法注释模板可以保证注释风格的一致性提高效率。但切记模板生成的是骨架核心的“为什么”还需要你手动补充。4.3 注释不能弥补糟糕的代码这是最关键的一点。很多人试图用一大堆注释来解释一段混乱、冗长的代码。这是本末倒置。正确的做法是先尽力重构代码让代码本身变得清晰。当你发现需要写很多注释才能说清楚一段代码在干什么时那通常是一个强烈的信号这段代码应该被重写。干净、表达力强的代码其需要的注释量会大大减少。5. 结构的整洁组织代码降低认知负荷代码结构决定了人们如何浏览和理解你的项目。混乱的结构就像把书乱扔在房间里找什么都费劲。热搜词中的“修改结构”、“结构体”、“包注释”都指向了对代码组织管理的需求。5.1 文件与目录结构按概念分层而非按类型一个常见的反模式是将所有相同类型的文件放在一起比如src/ ├── controllers/ ├── models/ ├── views/这在小型项目中或许可行但随着项目增长当你需要修改一个“用户”相关的功能时你不得不在controllers/、models/、views/三个目录间来回跳转认知负荷很高。更推荐的方式是按功能或业务模块组织类似于“领域驱动设计”中的限界上下文思想但不用那么复杂src/ ├── user/ │ ├── UserController.js │ ├── UserService.js │ ├── UserModel.js │ ├── user.routes.js │ └── tests/ ├── order/ │ ├── OrderController.js │ ├── OrderService.js │ ├── OrderModel.js │ ├── order.routes.js │ └── tests/ └── shared/ ├── utils/ └── constants/这样所有与“用户”相关的代码都聚集在user/目录下修改功能时上下文高度集中。shared/目录用于存放真正被多个模块复用的通用代码。5.2 代码格式一致性就是一切格式混乱的代码会极大地干扰阅读。好在这件事完全可以交给工具自动化无需争论。使用代码格式化工具如Prettier前端、BlackPython、gofmtGo。在项目中配置好并在提交代码前自动运行。这能消除所有关于缩进、空格、换行、引号的争论让团队产出风格完全一致的代码。遵循语言社区约定如Python的PEP8Java的Google Style Guide。这些约定是无数开发者总结出的最佳可读性实践。5.3 消除重复DRY原则DRYDon‘t Repeat Yourself是软件工程的基本原则。重复的代码是维护的噩梦当你需要修改逻辑时必须记住修改所有重复的地方极易出错。识别重复不仅仅是完全相同的代码行。结构重复相同的代码模式只是变量名不同和概念重复用不同的代码实现了相同的业务规则同样有害。如何消除提取函数/方法将重复的代码块提取成一个独立的函数。使用模板/泛型对于处理不同类型但逻辑相同的代码。创建基类或公用组件对于面向对象编程中多个子类的共同行为。配置化将硬编码的、可能变化的值如字符串常量、魔法数字提取到配置文件或常量定义中。热搜中的“字段注释”有时就是为了解释这些魔法数字的含义更好的做法是将其定义为有名字的常量如MAX_RETRY_TIMES 3代码直接使用MAX_RETRY_TIMES无需注释。5.4 依赖管理保持单向与松耦合这是向“架构”概念迈进的一小步但理解起来并不难。依赖方向让高层模块如业务逻辑依赖低层模块如工具类、数据库访问而不是反过来。避免循环依赖A依赖BB又依赖A这会导致代码难以理解和测试。松耦合模块之间通过清晰的接口Interface或抽象类进行通信而不是直接依赖具体的实现类。这符合“依赖倒置原则”。例如一个OrderService应该依赖一个PaymentGateway接口而不是具体的PayPalGateway类。这样更换支付平台时只需提供一个新的实现类而无需修改OrderService的代码。减少全局状态全局变量和单例模式会使组件间隐式耦合难以追踪状态变化和进行单元测试。尽量通过参数传递依赖或者使用依赖注入容器来管理。保持代码结构的整洁相当于给你的项目绘制了一张清晰的地图让任何新加入的开发者都能快速找到方向而不是在迷宫中摸索。6. 实战演练重构一段“脏代码”让我们把上面的习惯应用到一个具体场景。假设我们有一段处理用户订单的“脏代码”def handle(o): # o是订单字典 if o[‘status‘] ‘new‘: # 算钱 total 0 for i in o[‘items‘]: total i[‘price‘] * i[‘qty‘] if o[‘user‘][‘vip‘]: total total * 0.9 # 扣库存 for i in o[‘items‘]: db.exec(f“UPDATE stock SET qty qty - {i[‘qty‘]} WHERE id {i[‘id‘]}“) # 记日志 with open(‘order.log‘, ‘a‘) as f: f.write(f“Order {o[‘id‘]} processed, total: {total}\n“) o[‘total‘] total o[‘status‘] ‘processed‘ return o这段代码的问题非常典型函数过长、命名糟糕oi、注释无用、混合了多种职责计算、数据库更新、日志记录、使用魔法字符串和数字‘new‘,0.9、SQL拼接有安全风险。第一步改善命名和提取常量def handle_order(order_data): PROCESSED_STATUS ‘processed‘ NEW_STATUS ‘new‘ VIP_DISCOUNT_RATE 0.9 if order_data[‘status‘] NEW_STATUS: ...第二步拆分函数单一职责我们先识别出三个独立的任务计算总额、扣减库存、记录日志。def calculate_order_total(order_data, vip_discount_rate): total 0 for item in order_data[‘items‘]: total item[‘price‘] * item[‘quantity‘] if order_data[‘user‘][‘is_vip‘]: total total * vip_discount_rate return total def reduce_inventory(order_data, db_connection): for item in order_data[‘items‘]: # 使用参数化查询防止SQL注入 db_connection.execute( “UPDATE stock SET quantity quantity - ? WHERE product_id ?“, (item[‘quantity‘], item[‘product_id‘]) ) def log_order_processing(order_id, total_amount, log_file_path‘order.log‘): import datetime log_entry f“{datetime.datetime.now()}: Order {order_id} processed, total: {total_amount}\n“ with open(log_file_path, ‘a‘) as log_file: log_file.write(log_entry)第三步重构主函数并处理数据依赖现在主函数变得非常清晰def process_new_order(order_data, db_connection): “““处理状态为‘new‘的订单计算总额、扣库存并记录日志。“““ NEW_STATUS ‘new‘ PROCESSED_STATUS ‘processed‘ VIP_DISCOUNT_RATE 0.9 if order_data[‘status‘] ! NEW_STATUS: # 如果不是新订单直接返回或抛出异常 return order_data # 计算订单总额 order_total calculate_order_total(order_data, VIP_DISCOUNT_RATE) # 扣减库存 reduce_inventory(order_data, db_connection) # 记录处理日志 log_order_processing(order_data[‘id‘], order_total) # 更新订单状态和总额 order_data[‘total_amount‘] order_total order_data[‘status‘] PROCESSED_STATUS return order_data对比与收获可读性新代码的函数名和变量名清晰地表达了意图。主函数process_new_order读起来像一份说明书。可维护性每个函数只做一件事。如果需要修改折扣逻辑只需改动calculate_order_total如果需要换用不同的日志系统只需修改log_order_processing。可测试性现在可以轻松地为calculate_order_total编写单元测试而无需连接真实的数据库或操作文件系统。安全性消除了SQL注入漏洞。复用性calculate_order_total和log_order_processing函数可以在其他需要类似功能的地方被复用。这个重构过程没有用到任何高深的架构知识仅仅应用了命名、函数拆分、注释和结构优化这些基本习惯就带来了质的提升。7. 将这些习惯融入你的工作流知道这些习惯是一回事坚持实践是另一回事。以下是一些让习惯落地的具体建议1. 利用工具进行“被动”约束Linter代码检查工具如ESLintJavaScript、PylintPython、CheckstyleJava。在IDE中集成或在CI/CD流水线中配置自动检查命名规范、代码复杂度、未使用的变量等问题在编码时即时反馈。Formatter代码格式化工具如前所述用Prettier、Black等工具统一格式省去手动调整的麻烦。IDE智能提示与重构功能现代IDE如VS Code, IntelliJ IDEA的重命名Rename、提取函数Extract Function、提取变量Extract Variable等功能极其强大。善用它们重构的成本会大大降低。2. 进行主动的代码审查Code Review 代码审查是提升代码质量最有效的实践之一。在Review时不要只关注功能是否正确要将“干净代码”习惯作为重要的审查维度“这个函数名能更清晰地表达它的作用吗”“这个函数是不是太长了能否拆分成几个更小的函数”“这里的魔法数字86400是不是应该定义成常量SECONDS_PER_DAY”“这段注释解释的是‘为什么’还是重复的‘是什么’” 把Review过程当作一个互相学习、共同提升代码标准的机会。3. 小步重构持续进行 不要试图一次性重构整个庞大的遗留系统那会让人望而却步且风险极高。采用“童子军军规”每次修改代码时都让它的状态比你来时更好一点。比如你今天为了修复一个bug需要阅读并修改一个200行的函数。在修复之后花10分钟时间把这个函数里你最看不顺眼的一小部分比如一个30行的循环提取成一个新函数。日积月累代码库的健康度会稳步提升。4. 编写代码时的“心流”自问 在敲下每一行代码时养成快速自问的习惯“我起的这个名字三个月后的我还能看懂吗”“这个函数现在是在做一件事还是已经悄悄开始做第二件了”“我在这里写注释是因为代码太复杂无法表达还是我懒得把代码写清楚”最后记住干净代码的终极目标不是追求形式上的完美而是为了降低认知负荷提升开发效率减少缺陷。它是对未来负责也是对与你协作的同事的尊重。开始实践吧哪怕从今天起只为新写的代码起一个更好的名字开始你会立刻感受到它带来的正向反馈。
返回列表