Czysty kod: funkcje (w JavaScript)
Czy jesteś zmęczony zmaganiem się ze złożonymi, trudnymi do zrozumienia funkcjami w swojej bazie kodu? Czy chcesz pisać funkcje, które są łatwiejsze w utrzymaniu, wielokrotnego użytku i mniej podatne na błędy? Jeśli tak, to musisz zapoznać się z „Czystym kodem: funkcje”. Postępując zgodnie z najlepszymi praktykami przedstawionymi w książce „Czysty kod” Roberta C. Martina, możesz nauczyć się pisać funkcje, które są łatwiejsze do odczytania, przetestowania i utrzymania. W tym poście na blogu przyjrzymy się kluczowym koncepcjom funkcji czystego kodu i przedstawimy praktyczne wskazówki i przykłady, które pomogą Ci zaimplementować te koncepcje we własnym kodzie. Z naszą pomocą możesz przenieść swoje umiejętności kodowania na wyższy poziom i pisać funkcje, z którymi praca jest przyjemnością. Czy jesteś gotowy, aby dowiedzieć się więcej o funkcjach czystego kodu? Zanurzmy się!
Spis treści
· Małe funkcje są lepsze niż duże
· Funkcje powinny robić jedną rzecz
· Funkcje powinny mieć nazwy opisowe
· Funkcje powinny mieć ograniczoną liczbę argumentów
· Funkcje nie powinny mieć skutków ubocznych
· Funkcje nie powinny polegać na zmiennych globalnych
· Funkcje nie powinny być zbyt Głęboko zagnieżdżone
· Używaj wyjątków, a nie kodów błędów
· Wnioski
Małe funkcje są lepsze niż duże
Małe funkcje są łatwiejsze do odczytania i zrozumienia, ponieważ zwykle robią jedną rzecz i mają jasny cel. Ułatwia to również ich testowanie i konserwację, ponieważ jest mniej kodu do przejrzenia lub modyfikacji. Z drugiej strony duże funkcje mogą być bardziej złożone i trudniejsze do zrozumienia, co może prowadzić do błędów.
// 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;
}
Funkcja getTotalPrice()pobiera tablicę zamówień i zwraca ich łączną cenę, używając funkcji reduce()do sumowania ceny każdego zamówienia. Funkcja getOrderIds()pobiera tablicę zamówień i zwraca tablicę ich identyfikatorów za pomocą map()funkcji. Funkcja getOrderCount()pobiera tablicę zamówień i zwraca ich liczbę za pomocą lengthwłaściwości. Na koniec generateOrderSummary()funkcja pobiera tablicę zamówień i wykorzystuje trzy mniejsze funkcje do wygenerowania ciągu podsumowującego. Funkcja logOrderSummary()pobiera tablicę zamówień i rejestruje ciąg podsumowania w konsoli.
Funkcje powinny robić jedną rzecz
Każda funkcja powinna mieć jasny i wyraźny cel. Gdy funkcja wykonuje wiele czynności, jej zrozumienie, przetestowanie i utrzymanie może stać się trudniejsze. Dzieląc większe zadanie na mniejsze funkcje, możemy stworzyć bardziej czytelny i łatwiejszy w utrzymaniu kod.
// 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);
}
Funkcja splitInput()pobiera ciąg i używa split()metody do podzielenia go na tablicę wierszy. Funkcja trimLines()pobiera tablicę wierszy i używa map()metody do usuwania wszelkich białych znaków z początku i końca każdego wiersza. Funkcja removeEmptyLines()pobiera tablicę wierszy i używa filter()metody do usunięcia pustych wierszy. Funkcja formatLines()przyjmuje tablicę linii i używa map()metody do dodania >znaku na początku każdej linii. Funkcja joinLines()pobiera tablicę wierszy i używa join()metody do łączenia ich w pojedynczy łańcuch oddzielony znakami nowej linii. Na koniec processUserInput()funkcja wykorzystuje mniejsze funkcje do przetwarzania ciągu wejściowego.
Funkcje powinny mieć nazwy opisowe
Nazwy funkcji powinny dokładnie opisywać, co robi funkcja i powinny być łatwe do zrozumienia bez konieczności czytania implementacji funkcji. Opisowa nazwa funkcji może sprawić, że kod będzie bardziej zrozumiały i zmniejszy potrzebę komentarzy.
// Function with a vague name
function processData(data) {
// ...
}
// --------------------------------------------------------------
// Refactored with a descriptive name
function processUserData(userData) {
// ...
}
Funkcje powinny mieć ograniczoną liczbę argumentów
Funkcje, które przyjmują dużą liczbę argumentów, mogą być trudniejsze w użyciu, zrozumieniu i przetestowaniu. Jeśli to możliwe, staraj się, aby liczba argumentów nie przekraczała trzech lub czterech. Jednym ze sposobów zmniejszenia liczby argumentów jest przekazanie ich jako pojedynczego obiektu lub struktury danych.
// 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);
Funkcje nie powinny mieć skutków ubocznych
Efekt uboczny występuje, gdy funkcja modyfikuje dowolny stan poza własnym zakresem. Efekty uboczne mogą utrudniać testowanie i wnioskowanie o zachowaniu funkcji, a także mogą prowadzić do nieoczekiwanego zachowania. Funkcje powinny działać tylko na własnych parametrach i zmiennych lokalnych i nie powinny modyfikować żadnego stanu poza swoim zakresem.
// 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
Funkcje nie powinny opierać się na zmiennych globalnych
Zmienne globalne utrudniają zrozumienie i testowanie funkcji oraz mogą prowadzić do nieoczekiwanego zachowania. Funkcje powinny działać tylko na własnych parametrach i zmiennych lokalnych i nie powinny polegać na stanie globalnym. Ułatwia to również ponowne użycie funkcji w różnych kontekstach.
// 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
Funkcje nie powinny być zbyt głęboko zagnieżdżone
Głęboko zagnieżdżone funkcje mogą być trudniejsze do odczytania i zrozumienia, a także prowadzić do złożonych struktur kontroli. Funkcje powinny być zorganizowane w sposób ułatwiający zrozumienie przepływu sterowania, bez konieczności mentalnego śledzenia wielu poziomów zagnieżdżonych funkcji.
// 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
Używaj wyjątków, a nie kodów błędów
Kody błędów mogą utrudniać zrozumienie kodu i mogą prowadzić do skomplikowanych struktur kontrolnych. Wyjątki pozwalają na bardziej naturalny przepływ sterowania i łatwiejszą obsługę błędów. Funkcje powinny zgłaszać wyjątki, aby wskazać błędy lub nieoczekiwane zachowanie, zamiast zwracać kody błędów lub wartości.
// 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
}
Wniosek
Podsumowując, ważne jest przestrzeganie najlepszych praktyk podczas pisania funkcji w celu poprawy ogólnej jakości naszej bazy kodu. W ten sposób możemy zapewnić, że nasze funkcje są nie tylko czytelne i wielokrotnego użytku, ale także możliwe do utrzymania w dłuższej perspektywie. Może to pomóc zmniejszyć liczbę błędów i błędów, które mogą wynikać z źle napisanego kodu, co może zaoszczędzić czas i zasoby na dłuższą metę.
Warto zauważyć, że czysty kod to nie tylko pisanie kodu, który działa; chodzi również o pisanie kodu, który można łatwo zrozumieć, przetestować i modyfikować w miarę upływu czasu. Dlatego zaleca się poświęcenie czasu na uważne rozważenie najlepszych praktyk opisanych powyżej podczas pisania funkcji, aby zoptymalizować jakość i łatwość konserwacji kodu.

![Czym w ogóle jest lista połączona? [Część 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































