Написание чистого и поддерживаемого кода является важным аспектом разработки программного обеспечения. Чистый код не только упрощает его понимание и поддержку, но также помогает снизить риск появления ошибок и уязвимостей в системе безопасности. В этой статье мы рассмотрим некоторые из лучших идей из книги «Чистый код» Роберта С. Мартина для написания чистого кода, а также примеры Typescript.
Имена функций
Дайте вашим функциям описательные и осмысленные имена. Имена функций должны описывать, что делает функция, чтобы с первого взгляда было легче понять код.
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}
Это всего лишь несколько примеров лучших практик написания чистого кода с помощью TypeScript. Следуя этим рекомендациям, вы сможете писать код, который легко понять, поддерживать и масштабировать.
Кроме того, важно отметить, что эти методы не являются специфическими для TypeScript, они обычно рекомендуются для написания чистого и поддерживаемого кода на любом языке программирования.