快看,我的代码能“自己说话”!

2024年 6月 5日 49.8k 0

开发人员什么时候最崩溃?

别人我不知道,就我而言,要是我耗费了几个小时来研究代码,试图破译它的目的,却迟迟不得门路,真是恨不得找到写代码的那个家伙,让他回炉重造。

今天我们将在这篇文章中探讨如何编写自文档化的代码,让代码自己会说话。

什么是自文档化的代码?

自文档化的代码是以清晰、富有表现力的方式编写的代码,无需大量的注释和外部文档,就能让人理解代码的目的和功能。

自文档化代码的特点:

  • 可读性:代码易于阅读和理解,一目了然
  • 富有表现力:清楚传达代码的意图和目的
  • 可维护:代码易于修改和更新,不会引入错误和重大更改

自文档化代码的重要性

编写自文档化代码的好处:

  • 开发人员可以快速掌握代码的目的和功能,减少理解和使用代码库所需的脑力劳动。
  • 新的团队成员可以更快地上手,因为他们不需要特别依赖外部文档。
  • 最大限度地减少团队成员之间的误解,促进对代码库的共同理解。
  • 即使随着时间的推移,维护和更新也依然方便简单,因为开发人员可以快速理解现有代码并做出明智的更改。
  • 如何编写自文档化的代码

    了解了什么样的代码是自文档化的代码之后,敲黑板,我们的重点来了,那么,怎么编写这样可以“自己说话”的代码呢?

    1.使用有意义的名字

    使代码自文档化的最有效方法之一是对变量、函数、类和模块使用有意义的名称。

    请看以下示例:

    // Bad
    const x = 5;
    const y = 10;
    const z = x + y;
    
    // Good
    const numberOfItems = 5;
    const itemPrice = 10;
    const totalCost = numberOfItems * itemPrice;

    在Good示例中,变量名称numberOfItems、itemPrice、totalCost清楚地传达了用途,理解起来非常方便。

    2. 编写小而精的函数

    编写小而精的函数是自文档化代码的另一个关键。函数应当功能单一,并准确命名以反映其目的。

    例如:

    // Bad
    function processData(data: any): any {
        // ...
        // Lots of complex logic
        // ...
        return result;
    }
    
    // Good
    function extractRelevantFields(data: Record): Record {
        // ...
        return relevantFields;
    }
    
    function applyBusinessRules(relevantFields: Record): Record {
        // ...
        return processedData;
    }
    
    function formatOutput(processedData: Record): string {
        // ...
        return formattedResult;
    }

    通过将大函数分解为名称更具描述性的小而精函数,代码明显更可读了。

    3. 使用描述性的函数名称和方法名称

    在命名函数和方法时,使用描述性的名称可以更加清楚地传达其目的和操作。注意:应尽量避免使用通用名称,如handle()或process()这样的写法。

    请看示例:

    // Bad
    function handleInput(input: string): void {
        // ...
    }
    
    // Good
    function validateUserCredentials(username: string, password: string): boolean {
        // ...
    }

    看到了吗?描述性名称validateUserCredentials清楚地表明了函数的作用。现在,我们哪还需要额外的注释?

    4. 利用 TypeScript 的类型系统

    TypeScript 强大的类型系统可以大大增强代码的自文档化。所以要懂得利用工具,借助 TypeScript 的功能,使代码更具表现力,及早发现潜在的错误。

    例如:

    • 接口和类型:使用接口和自定义类型来定义数据结构的形状。
    interface User {
        id: number;
        name: string;
        email: string;
    }
    
    function getUserById(id: number): User | undefined {
        // ...
    }
    • 枚举:利用枚举来表示一组固定的值,提供了一种清晰易读的方式来处理不同的方案。
    enum PaymentStatus {
        Pending = 'pending',
        Completed = 'completed',
        Failed = 'failed',
    }
    
    function processPayment(status: PaymentStatus): void {
        // ...
    }
    • 类型推断:尽量使用 TypeScript 推断类型,因为可以精简代码。
    // Bad
    const count: number = 10;
    const message: string = 'Hello, world!';
    
    // Good
    const count = 10;
    const message = 'Hello, world!';

    5. 使用强类型 ID

    在 TypeScript 中处理 ID 时,建议使用强类型 ID,不要直接上字符串和数字。强类型 ID 提供了额外的类型安全性。

    实现强类型 ID 的一种方法是使用不透明的类型:

    // user.ts
    export type UserId = string & { readonly __brand: unique symbol };
    
    export function createUserId(id: string): UserId {
        return id as UserId;
    }
    
    // post.ts
    export type PostId = string & { readonly __brand: unique symbol };
    
    export function createPostId(id: string): PostId {
        return id as PostId;
    }

    这里我们使用强类型 ID,确保了UserId只分配给需要UserId的属性和函数,PostId只分配给需要PostId的属性和函数。

    function getUserById(userId: UserId): User | undefined {
        // ...
    }
    
    const userId = createUserId('user-123');
    const postId = createPostId('post-456');
    
    getUserById(userId); // No error
    getUserById(postId); // Error: Argument of type 'PostId' is not assignable to parameter of type 'UserId'.

    强类型的 ID 有助于在编译时捕获潜在错误,使代码更具表现力和自文档化。

    但是,与使用简单的字符串类型相比,强类型 ID确实会引入一些开销。所以我们需要根据项目的需求和规模权衡利弊。

    6. 增强团队凝聚力

    团队工作的时候,建立一致性至关重要,不然你有你的标准,我有我的约定,程序还怎么跑得起来?

    关于一致性,有一个非常重要的方面是命名约定。最好的做法是建立一个风格指南,定义变量、函数、类和其他实体的命名方式。

    例如,可以使用类似这样的术语:

    • get:检索 API 或数据源中的单个值。
    • list:检索 API 或数据源中的一组值。
    • patch:部分更新现有实体和对象。
    • upsert:更新现有实体,如果不存在,则插入新实体。

    统一执行术语可以确保整个代码库的一致性。

    例如:

    function getUser(userId: UserId): Promise {
        // ...
    }
    
    function listUsers(): Promise {
        // ...
    }
    
    function patchUser(userId: UserId, updatedData: Partial): Promise {
        // ...
    }

    怎么样,是不是明显更易于大家理解了呢。

    除了命名约定之外,还可以为代码库的其他方面制定准则,例如文件和文件夹结构、注释、错误处理、测试和代码格式等。

    在团队中工作时,我们有时候可能不习惯或者不赞同正在执行的某个约定。但是,重要的是要记住,一致性和协作对于项目的成功至关重要。

    即使你有不同的偏好或编码风格,也应该遵守约定。从而保持一个有凝聚力的代码库,减少混淆。

    7. 复杂场景使用 JSDoc 或 TSDoc

    虽然自文档化的代码非常优秀,但是不可否认的是,在某些情况下该上文档就得上文档,例如如果遇到复杂的算法和复杂的业务逻辑,你不写注释,简直就不给后来人活路。

    在这种情况下,可以考虑使用 JSDoc 或 TSDoc 来提供清晰简洁的文档。

    /**
    * Calculates the Fibonacci number at the given position.
    *
    * @param {number} position - The position of the Fibonacci number to calculate.
    * @returns {number} The Fibonacci number at the specified position.
    */
    function fibonacci(position: number): number {
    if (position

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论