Чистый код: функции (на Javascript)

Mar 26 2023
Вы устали бороться со сложными, трудными для понимания функциями в кодовой базе? Вы хотите писать функции, которые легче поддерживать, повторно использовать и менее подвержены ошибкам? Если да, то вам нужно узнать о «Чистом коде: Функции». Следуя рекомендациям, изложенным в книге «Чистый код» Роберта С.

Вы устали бороться со сложными, трудными для понимания функциями в кодовой базе? Вы хотите писать функции, которые легче поддерживать, повторно использовать и менее подвержены ошибкам? Если да, то вам нужно узнать о «Чистом коде: Функции». Следуя рекомендациям, изложенным в книге Роберта С. Мартина «Чистый код», вы сможете научиться писать функции, которые легче читать, тестировать и поддерживать. В этом сообщении блога мы рассмотрим ключевые концепции функций чистого кода и предоставим вам практические советы и примеры, которые помогут вам реализовать эти концепции в вашем собственном коде. С нашей помощью вы сможете поднять свои навыки кодирования на новый уровень и писать функции, с которыми приятно работать. Итак, вы готовы узнать о функциях чистого кода? Давайте погрузимся!

Содержание

· Маленькие функции лучше больших
· Функции должны делать одно дело
· Функции должны иметь описательные имена
· Функции должны иметь ограниченное количество аргументов
· Функции не должны иметь побочных эффектов
· Функции не должны полагаться на глобальные переменные
· Функции не должны быть слишком Глубокая вложенность
· Используйте исключения, а не коды ошибок
· Заключение

Маленькие функции лучше, чем большие

Маленькие функции легче читать и понимать, потому что они обычно делают одну вещь и имеют четкую цель. Это также упрощает их тестирование и поддержку, поскольку требуется меньше кода для просмотра или изменения. С другой стороны, большие функции могут быть более сложными и трудными для понимания, что может привести к ошибкам.

// 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;
}

Функция getTotalPrice()берет массив ордеров и возвращает их общую цену, используя функцию reduce()для суммирования цены каждого ордера. Функция getOrderIds()принимает массив заказов и возвращает массив их идентификаторов с помощью map()функции. Функция getOrderCount()принимает массив заказов и возвращает их количество, используя свойство length. Наконец, generateOrderSummary()функция принимает массив заказов и использует три меньшие функции для создания итоговой строки. Функция logOrderSummary()принимает массив заказов и выводит сводную строку на консоль.

Функции должны делать одну вещь

Каждая функция должна иметь четкую и четкую цель. Когда функция выполняет несколько функций, ее становится сложнее понять, протестировать и поддерживать. Разбивая большую задачу на более мелкие функции, мы можем создавать более читаемый и удобный для сопровождения код.

// 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);
}

Функция splitInput()принимает строку и использует split()метод, чтобы разбить ее на массив строк. Функция trimLines()принимает массив строк и использует map()метод для удаления всех пробелов в начале и в конце каждой строки. Функция removeEmptyLines()принимает массив строк и использует filter()метод для удаления любых пустых строк. Функция formatLines()принимает массив строк и использует map()метод для добавления >символа в начало каждой строки. Функция joinLines()принимает массив строк и использует join()метод для их объединения в одну строку, разделенную символами новой строки. Наконец, processUserInput()функция использует меньшие функции для обработки входной строки.

Функции должны иметь описательные имена

Имена функций должны точно описывать то, что делает функция, и должны быть простыми для понимания без необходимости читать реализацию функции. Описательное имя функции может помочь сделать код более понятным и уменьшить потребность в комментариях.

// Function with a vague name
function processData(data) {
  // ...
}
// --------------------------------------------------------------
// Refactored with a descriptive name
function processUserData(userData) {
  // ...
}

Функции должны иметь ограниченное количество аргументов

Функции, которые принимают большое количество аргументов, сложнее использовать, понимать и тестировать. Старайтесь, чтобы число аргументов не превышало трех или четырех, если это возможно. Один из способов уменьшить количество аргументов — передать их как один объект или структуру данных.

// 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);

Функции не должны иметь побочных эффектов

Побочный эффект возникает, когда функция изменяет любое состояние за пределами своей области. Побочные эффекты могут затруднить тестирование и анализ поведения функции, а также могут привести к неожиданному поведению. Функции должны работать только со своими собственными параметрами и локальными переменными и не должны изменять какое-либо состояние за пределами своей области.

// 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

Функции не должны полагаться на глобальные переменные

Глобальные переменные усложняют понимание и тестирование функций и могут привести к неожиданному поведению. Функции должны работать только со своими собственными параметрами и локальными переменными и не должны полагаться на глобальное состояние. Это также упрощает повторное использование функции в различных контекстах.

// 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

Функции не должны быть слишком глубоко вложены

Глубоко вложенные функции могут быть труднее читать и понимать, а также приводить к сложным структурам управления. Функции должны быть структурированы таким образом, чтобы упростить понимание потока управления без необходимости мысленно отслеживать несколько уровней вложенных функций.

// 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

Используйте исключения, а не коды ошибок

Коды ошибок могут затруднить понимание кода и привести к сложным структурам управления. Исключения обеспечивают более естественный поток управления и упрощают обработку ошибок. Функции должны генерировать исключения, чтобы указать на ошибки или неожиданное поведение, а не возвращать коды ошибок или значения.

// 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
}

Заключение

В заключение, важно следовать рекомендациям при написании функций, чтобы улучшить общее качество нашей кодовой базы. Поступая таким образом, мы можем гарантировать, что наши функции будут не только читаемыми и пригодными для повторного использования, но и поддерживаемыми в долгосрочной перспективе. Это может помочь уменьшить количество багов и ошибок, которые могут возникнуть из-за плохо написанного кода, что может сэкономить время и ресурсы в долгосрочной перспективе.

Стоит отметить, что чистый код — это не только написание работающего кода; речь также идет о написании кода, который можно легко понять, протестировать и модифицировать с течением времени. Поэтому рекомендуется уделить время тщательному рассмотрению изложенных выше передовых практик при написании функций, чтобы оптимизировать качество и удобство сопровождения вашего кода.