罗伯特·C·马丁(Robert C. Martin
#javascript #cleancode #bestpractices

编写干净可维护的代码是软件开发的重要方面。干净的代码不仅使理解和维护更容易,还可以帮助降低错误和安全漏洞的风险。在本文中,我们将探讨罗伯特·C·马丁(Robert C.

功能名称

给您的功能描述性和有意义的名称。函数名称应描述该函数的功能,从而更容易浏览代码。

function calculateTotalPrice(items: Item[]): number { ... }
function sendEmailToUser(user: User, message: string): void { ... }

功能长度

功能长度:保持功能短而简洁。一个好的经验法则是将它们保持在20行代码下。

// bad example
function processData(data: any[]) {
    data.sort((a, b) => a.value - b.value);
    const filteredData = data.filter(item => item.value > 10);
    const result = filteredData.map(item => item.name).join(", ");
    return result;
}

// good example
function sortData(data: any[]) {
    return data.sort((a, b) => a.value - b.value);
}

function filterData(data: any[]) {
    return data.filter(item => item.value > 10);
}

function extractNames(data: any[]) {
    return data.map(item => item.name).join(", ");
}

function processData(data: any[]) {
    const sortedData = sortData(data);
    const filteredData = filterData(sortedData);
    const result = extractNames(filteredData);
    return result;
}

单一责任原则

每个功能应具有一个明确的责任。这使维护和更新您的代码以及在代码其他部分中重复使用功能变得更加容易。

// bad example
class User {
    private name: string;
    private email: string;
    private password: string;

    constructor(name: string, email: string, password: string) {
        this.name = name;
        this.email = email;
        this.password = password;
    }

    public getName(): string {
        return this.name;
    }

    public getEmail(): string {
        return this.email;
    }

    public hashPassword(): string {
        // hash password logic
        return this.password;
    }
}

// good example
class User {
    private name: string;
    private email: string;
    private password: string;

    constructor(name: string, email: string, password: string) {
        this.name = name;
        this.email = email;
        this.password = password;
    }

    public getName(): string {
        return this.name;
    }

    public getEmail(): string {
        return this.email;
    }
}

class PasswordHasher {
    public static hashPassword(password: string): string {
        // hash password logic
        return password;
    }
}

可读性

通过使用清晰和简洁的变量名称,在必要时添加注释,并将复杂的功能分解为较小,更易于管理的作品。

// Clear and concise variable names
const userName = getUserName(userId);
const formattedDate = formatDate(date);

// Comments to explain complex logic
function calculateDiscount(price: number, quantity: number): number {
  // Apply 10% discount for orders over 100 items
  if (quantity >= 100) {
    return price * 0.9;
  }

  // No discount for orders under 100 items
  return price;
}

// Breaking up complex functions into smaller pieces
function applyDiscount(price: number, discount: number): number {
  return price * (1 - discount);
}

function calculateDiscount(price: number, quantity: number): number {
  if (quantity >= 100) {
    return applyDiscount(price, 0.1);
  }

  return price;
}

避免魔术数字

避免使用代码中没有明确含义的魔法数字,或硬编码值。相反,使用描述性常数或枚举使代码更可读和可维护。例如:

// Avoid magic numbers
function calculateDiscount(price: number, quantity: number): number {
  if (quantity >= 100) {
    return price * 0.9;
  }

  if (quantity >= 50) {
    return price * 0.95;
  }

  return price;
}

// Use descriptive constants
const MIN_QUANTITY_FOR_10_PERCENT_DISCOUNT = 100;
const MIN_QUANTITY_FOR_5_PERCENT_DISCOUNT = 50;

function calculateDiscount(price: number, quantity: number): number {
  if (quantity >= MIN_QUANTITY_FOR_10_PERCENT_DISCOUNT) {
    return price * 0.9;
  }

  if (quantity >= MIN_QUANTITY_FOR_5_PERCENT_DISCOUNT) {
    return price * 0.95;
  }

  return price;
}

干燥原理

不要重复自己。尽可能多地重复使用代码,然后重构代码重复使用的函数和模块。

// bad example
function validateEmail(email: string): boolean {
    const regex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
    return regex.test(email);
}

function validatePassword(password: string): boolean {
    const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
    return regex.test(password);
}

// good example
const emailRegex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

function validate(value: string, regex: RegExp): boolean {
    return regex.test(value);
}

function validateEmail(email: string): boolean {
    return validate(email, emailRegex);
}

function validatePassword(password: string): boolean {
    return validate(password, passwordRegex);
}

错误处理

通过使用明确和描述性错误消息,在必要时记录错误以及使用适当的错误处理技术来防止代码崩溃,可以优雅地处理错误。例如:

// Use try-catch for synchronous errors
try {
  const user = getUser(userId);
  console.log(`User: ${user.name}`);
} catch (error) {
  console.error(`Error: ${error.message}`);
}

// Use promises and async-await for asynchronous errors
async function getOrders(userId: number): Promise<Order[]> {
  try {
    const orders = await fetchOrders(userId);
    return orders;
  } catch (error) {
    console.error(`Error: ${error.message}`);
    throw error;
  }
}

此外,仅在特殊情况下使用例外情况也是一种很好的做法。当期望经常发生错误时,请使用返回代码或错误对象而不是异常。例如:

// Use exceptions for exceptional situations
function divide(a: number, b: number): number {
  if (b === 0) {
    throw new Error("Cannot divide by zero");
  }
  return a / b;
}

// Use return codes or error objects for expected errors
interface Result {
  result: number;
  error: string | null;
}

function divideWithReturnCode(a: number, b: number): Result {
  if (b === 0) {
    return { result: 0, error: "Cannot divide by zero" };
  }
  return { result: a / b, error: null };
}

这种方法有助于提高代码的性能和可读性,因为例外具有性能成本,并且比返回代码或错误对象更难跟踪。此外,这也使理解代码的目的更加容易,因为错误处理逻辑显然与主要逻辑分开。

简单代码

保持代码简单明了。避免使用复杂的算法,复杂的控制流结构或不必要的抽象。以下是实现不良的示例:

// Bad Example: Complex for loop to sum an array of numbers
function sumArray(numbers: number[]): number {
  let sum = 0;
  for (let i = 0; i < numbers.length; i++) {
    for (let j = 0; j < numbers[i]; j++) {
      sum++;
    }
  }
  return sum;
}

和更好的实现:

// Simple for loop to sum an array of numbers
function sumArray(numbers: number[]): number {
  let sum = 0;
  for (const number of numbers) {
    sum += number;
  }
  return sum;
}

避免全局变量

避免使用全局变量,因为它们可以使您的代码难以维护和调试。相反,使用模块和功能封装状态和行为。

// bad example
let name = "John Doe";

function getName() {
    return name;
}

function setName(newName: string) {
    name = newName;
}

// good example
function createNameModule() {
    let name = "John Doe";

    return {
        getName: () => name,
        setName: (newName: string) => { name = newName; }
    }
}

const nameModule = createNameModule();
const currentName = nameModule.getName();
nameModule.setName("Jane Doe");

测试驱动的开发

首先编写测试,然后编写代码以使测试通过。这有助于确保您的代码按预期工作,并使其更易于维护和更新。

// good example
function add(a: number, b: number): number {
    return a + b;
}

describe("add", () => {
    it("should add two numbers", () => {
        const result = add(1, 2);
        expect(result).toBe(3);
    });
});

将最重要的代码保持在顶部

将最重要的代码放在文件的顶部,然后是不太重要的代码,以便读者可以快速找到并理解代码的核心部分。这使其他人更容易理解和维护您的代码。

// createAnimal.ts

// Good Example: Most important code at the top
function createAnimal(name: string, food: string, hours: number) {
  console.log(`Creating animal ${name}...`);
  eat(food);
  sleep(hours);
  console.log(`${name} has been created.`);
}

// Other functions and code
function eat(food: string) {
  console.log(`Eating ${food}...`);
}

function sleep(hours: number) {
  console.log(`Sleeping for ${hours} hours...`);
}

代码组织

将您的代码整合到不同的部分中,使阅读和维护更易于。将类似的功能和类别组合在一起,并用清晰有意义的评论将它们分开。

// Utility functions section
function getUserName(userId: number): string { ... }
function formatDate(date: Date): string { ... }

// Database access section
class UserRepository { ... }
class OrderRepository { ... }

代码格式

遵守一致的代码格式化样式,例如正确使用空间和凹痕。正确格式化代码可以使阅读,理解和维护更加容易。良好的代码格式样式应保持一致,清晰和简洁。例如:

// Good Example: Code Formatting
function calculateSum(numbers: number[]): number {
  let sum = 0;
  for (const number of numbers) {
    sum += number;
  }
  return sum;
}

// Bad Example: Code Formatting
function calculateSum(numbers:number[])
{let sum=0
for(const number of numbers)
{sum+=number}
return sum}

这些只是用打字稿编写干净代码的最佳实践的一些示例。通过遵循这些实践,您可以编写易于理解,维护和扩展的代码。

此外,重要的是要注意,这些实践不是特定于打字稿的,通常建议它们以任何编程语言编写清洁和可维护的代码。