Clean Code: Hàm (trong Javascript)
Bạn có cảm thấy mệt mỏi khi phải vật lộn với các hàm phức tạp, khó hiểu trong cơ sở mã của mình không? Bạn có muốn viết các chức năng dễ bảo trì hơn, có thể tái sử dụng và ít bị lỗi hơn không? Nếu vậy thì bạn cần tìm hiểu về “Clean Code: Functions”. Bằng cách làm theo các phương pháp hay nhất được nêu trong cuốn sách “Clean Code” của Robert C. Martin, bạn có thể học cách viết các hàm dễ đọc, kiểm tra và bảo trì hơn. Trong bài đăng trên blog này, chúng tôi sẽ khám phá các khái niệm chính về chức năng mã sạch và cung cấp cho bạn các mẹo và ví dụ thực tế để giúp bạn triển khai các khái niệm này trong mã của riêng mình. Với sự trợ giúp của chúng tôi, bạn có thể nâng kỹ năng mã hóa của mình lên một tầm cao mới và viết các hàm thú vị khi làm việc cùng. Vì vậy, bạn đã sẵn sàng tìm hiểu về các hàm mã sạch chưa? Hãy đi sâu vào!
Mục lục
· Các hàm nhỏ tốt hơn các hàm lớn
· Các hàm nên làm một việc
· Các hàm nên có tên mô tả
· Các hàm nên có một số đối số hạn chế
· Các hàm nên không có tác dụng phụ
· Các hàm không nên dựa vào các biến toàn cục
· Các hàm không nên quá nhiều Lồng sâu vào nhau
· Sử dụng ngoại lệ, không phải mã lỗi
· Kết luận
Chức năng nhỏ tốt hơn chức năng lớn
Các chức năng nhỏ dễ đọc và dễ hiểu hơn vì chúng thường làm một việc và có mục đích rõ ràng. Điều này cũng làm cho chúng dễ kiểm tra và bảo trì hơn vì có ít mã hơn để xem xét hoặc sửa đổi. Mặt khác, các chức năng lớn có thể phức tạp hơn và khó hiểu hơn, điều này có thể dẫn đến sai sót hoặc lỗi.
// Large function
function processOrders(orders) {
const total = orders.reduce((acc, order) => acc + order.price, 0);
const orderIds = orders.map(order => order.id);
const orderCount = orders.length;
const orderSummary = `You have ${orderCount} orders with IDs ${orderIds.join(', ')} for a total of $${total}.`;
console.log(orderSummary);
return orderSummary;
}
// --------------------------------------------------------------
// Refactored into smaller functions
function getTotalPrice(orders) {
return orders.reduce((acc, order) => acc + order.price, 0);
}
function getOrderIds(orders) {
return orders.map(order => order.id);
}
function getOrderCount(orders) {
return orders.length;
}
function generateOrderSummary(orders) {
const total = getTotalPrice(orders);
const orderIds = getOrderIds(orders);
const orderCount = getOrderCount(orders);
return `You have ${orderCount} orders with IDs ${orderIds.join(', ')} for a total of $${total}.`;
}
function logOrderSummary(orders) {
const orderSummary = generateOrderSummary(orders);
console.log(orderSummary);
return orderSummary;
}
Hàm getTotalPrice()lấy một mảng các đơn đặt hàng và trả về tổng giá của chúng bằng cách sử dụng reduce()hàm để tính tổng giá của từng đơn đặt hàng. Hàm getOrderIds()lấy một mảng các đơn đặt hàng và trả về một mảng id của chúng bằng cách sử dụng map()hàm. Hàm getOrderCount()lấy một mảng các đơn đặt hàng và trả về số lượng của chúng bằng cách sử dụng lengththuộc tính. Cuối cùng, generateOrderSummary()hàm nhận một mảng các đơn đặt hàng và sử dụng ba hàm nhỏ hơn để tạo chuỗi tóm tắt. Hàm logOrderSummary()lấy một mảng các đơn đặt hàng và ghi chuỗi tóm tắt vào bảng điều khiển.
Chức năng nên làm một việc
Mỗi chức năng nên có một mục đích rõ ràng và khác biệt. Khi một hàm thực hiện nhiều việc, nó có thể trở nên khó hiểu, khó kiểm tra và bảo trì hơn. Bằng cách chia nhỏ nhiệm vụ lớn hơn thành các chức năng nhỏ hơn, chúng ta có thể tạo mã dễ đọc và dễ bảo trì hơn.
// Function that performs multiple tasks
function processUserInput(input) {
const inputLines = input.split('\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n');
const trimmedLines = inputLines.map(line => line.trim());
const nonEmptyLines = trimmedLines.filter(line => line !== '');
const formattedLines = nonEmptyLines.map(line => `> ${line}`);
return formattedLines.join('\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n');
}
// --------------------------------------------------------------
// Refactored into smaller functions
function splitInput(input) {
return input.split('\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n');
}
function trimLines(lines) {
return lines.map(line => line.trim());
}
function removeEmptyLines(lines) {
return lines.filter(line => line !== '');
}
function formatLines(lines) {
return lines.map(line => `> ${line}`);
}
function joinLines(lines) {
return lines.join('\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n');
}
function processUserInput(input) {
const lines = splitInput(input);
const trimmedLines = trimLines(lines);
const nonEmptyLines = removeEmptyLines(trimmedLines);
const formattedLines = formatLines(nonEmptyLines);
return joinLines(formattedLines);
}
Hàm splitInput()lấy một chuỗi và sử dụng split()phương thức để tách nó thành một mảng các dòng. Hàm trimLines()lấy một mảng các dòng và sử dụng map()phương thức để xóa mọi khoảng trắng ở đầu và cuối mỗi dòng. Hàm removeEmptyLines()lấy một mảng các dòng và sử dụng filter()phương thức để xóa bất kỳ dòng trống nào. Hàm formatLines()lấy một mảng các dòng và sử dụng map()phương thức để thêm một >ký tự vào đầu mỗi dòng. Hàm joinLines()lấy một mảng các dòng và sử dụng join()phương thức để nối chúng thành một chuỗi duy nhất, được phân tách bằng các dòng mới. Cuối cùng, processUserInput()hàm sử dụng các hàm nhỏ hơn để xử lý chuỗi đầu vào.
Các chức năng nên có tên mô tả
Tên hàm phải mô tả chính xác chức năng của hàm và phải dễ hiểu mà không cần phải đọc cách triển khai của hàm. Tên hàm mô tả có thể giúp làm cho mã dễ hiểu hơn và giảm nhu cầu bình luận.
// Function with a vague name
function processData(data) {
// ...
}
// --------------------------------------------------------------
// Refactored with a descriptive name
function processUserData(userData) {
// ...
}
Các chức năng nên có một số lượng đối số hạn chế
Các hàm có số lượng lớn đối số có thể khó sử dụng, khó hiểu và khó kiểm tra hơn. Cố gắng giữ số lượng đối số dưới ba hoặc bốn, nếu có thể. Một cách để giảm số lượng đối số là chuyển chúng dưới dạng một đối tượng hoặc cấu trúc dữ liệu.
// Function with too many arguments
function createPerson(name, age, address, phone, email) {
// ...
}
// --------------------------------------------------------------
// Refactored to use an object instead
function createPerson(personData) {
// ...
}
// Now, we can pass an object with properties for name, age, address, phone, and email:
const personData = {
name: 'John Doe',
age: 30,
address: '123 Main St',
phone: '555-555-1234',
email: '[email protected]'
};
const person = createPerson(personData);
Các chức năng không nên có tác dụng phụ
Một tác dụng phụ xảy ra khi một chức năng sửa đổi bất kỳ trạng thái nào bên ngoài phạm vi của chính nó. Các tác dụng phụ có thể khiến việc kiểm tra và suy luận về hành vi của hàm trở nên khó khăn hơn và cũng có thể dẫn đến hành vi không mong muốn. Các hàm chỉ nên hoạt động trên các tham số và biến cục bộ của riêng chúng và không được sửa đổi bất kỳ trạng thái nào bên ngoài phạm vi của chúng.
// Example with side effects
let arr = [1, 2, 3];
function popLastElement() {
arr.pop(); // modifies the external state of `arr`
}
popLastElement();
console.log(arr); // Output: [1, 2]
// --------------------------------------------------------------
// Example without side effects
function getLastElement(arr) {
return arr[arr.length - 1]; // returns a new value without modifying the input
}
let arr = [1, 2, 3];
let lastElement = getLastElement(arr);
console.log(lastElement); // Output: 3
Các chức năng không nên dựa vào các biến toàn cầu
Các biến toàn cục khiến việc hiểu và kiểm tra các chức năng trở nên khó khăn hơn, đồng thời có thể dẫn đến hành vi không mong muốn. Các chức năng chỉ nên hoạt động trên các tham số và biến cục bộ của chính chúng và không nên dựa vào trạng thái toàn cầu. Điều này cũng giúp việc sử dụng lại chức năng trong các ngữ cảnh khác nhau dễ dàng hơn.
// Example relying on global state
let x = 0;
function increment() {
x++;
}
increment();
console.log(x); // Output: 1
// --------------------------------------------------------------
// Example without relying on global state
function increment(x) {
return x + 1;
}
let y = 0;
y = increment(y);
console.log(y); // Output: 1
Các chức năng không nên được lồng quá sâu
Các hàm được lồng sâu có thể khó đọc và khó hiểu hơn, cũng như dẫn đến các cấu trúc điều khiển phức tạp. Các chức năng phải được cấu trúc theo cách giúp bạn dễ dàng hiểu được luồng kiểm soát mà không cần phải theo dõi tinh thần nhiều cấp độ chức năng lồng nhau.
// Example with deeply nested functions
function add(a, b) {
function increment(x) {
return x + 1;
}
let c = increment(a);
let d = increment(b);
return c + d;
}
console.log(add(1, 2)); // Output: 5
// --------------------------------------------------------------
// Example with flatter function structure
function increment(x) {
return x + 1;
}
function add(a, b) {
let c = increment(a);
let d = increment(b);
return c + d;
}
console.log(add(1, 2)); // Output: 5
Sử dụng ngoại lệ, không phải mã lỗi
Các mã lỗi có thể khiến việc suy luận về mã trở nên khó khăn hơn và có thể dẫn đến các cấu trúc điều khiển phức tạp. Các ngoại lệ cho phép luồng điều khiển tự nhiên hơn và xử lý lỗi dễ dàng hơn. Các hàm nên đưa ra các ngoại lệ để chỉ ra lỗi hoặc hành vi không mong muốn, thay vì trả về mã hoặc giá trị lỗi.
// Example with error code
function divide(a, b) {
if (b === 0) {
return -1; // error code for divide by zero
}
return a / b;
}
let result = divide(4, 0);
if (result === -1) {
console.log("Error: Divide by zero"); // error handling with error code
}
// --------------------------------------------------------------
// Example with exception
function divide(a, b) {
if (b === 0) {
throw new Error("Divide by zero"); // throws an exception
}
return a / b;
}
try {
let result = divide(4, 0);
} catch (e) {
console.log("Error: " + e.message); // error handling with exception
}
Phần kết luận
Tóm lại, điều quan trọng là phải tuân theo các phương pháp hay nhất khi viết hàm để cải thiện chất lượng tổng thể của cơ sở mã của chúng ta. Bằng cách đó, chúng ta có thể đảm bảo rằng các chức năng của chúng ta không chỉ có thể đọc được và sử dụng lại được mà còn có thể duy trì lâu dài. Điều này có thể giúp giảm số lượng lỗi và lỗi có thể phát sinh do viết mã kém, giúp tiết kiệm thời gian và tài nguyên về lâu dài.
Điều đáng chú ý là mã sạch không chỉ là viết mã hoạt động; nó cũng là về việc viết mã có thể dễ hiểu, kiểm tra và sửa đổi theo thời gian. Do đó, bạn nên dành thời gian xem xét cẩn thận các phương pháp hay nhất được nêu ở trên khi viết hàm, nhằm tối ưu hóa chất lượng và khả năng bảo trì mã của bạn.

![Dù sao thì một danh sách được liên kết là gì? [Phần 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































