تسلط بر کد پاک: تمرینهای ضروری برای توسعهدهندگان

کد پاک سنگ بنای هر پروژه نرم افزاری موفق است. به عنوان یک توسعه دهنده، توانایی شما برای نوشتن کدهای تمیز و قابل نگهداری برای کارایی و طول عمر برنامه های شما بسیار مهم است. در این مقاله، ده نمونه از شیوههای کدنویسی خوب و بد در جاوا اسکریپت را بررسی میکنیم، که بر اهمیت نوشتن کد پاک و ارائه بینشهای عملی برای کمک به ارتقای مهارتهای توسعه خود تأکید میکند.
مثال ها
- نام متغیرهای توصیفی:
// Good:
const totalPrice = calculateTotalPrice(quantity, unitPrice);
// Bad:
const t = calcPrice(q, uP);
در یک مثال خوب، نام متغیرها توصیفی هستند و هدف خود را به وضوح بیان می کنند و خوانایی کد را افزایش می دهند. برعکس، مثال بد از اختصارات رمزآلود استفاده می کند و درک هدف کد را برای دیگران دشوار می کند.
// Good:
function greet(name) {
return `Hello, ${name}!`;
}
// Bad:
function greet(name){
return `Hello, ${name}!`
}
قالب بندی مداوم خوانایی و نگهداری کد را بهبود می بخشد. در یک مثال خوب، تورفتگی و فاصله گذاری مناسب استفاده شده است که ساختار کد را بهبود می بخشد. برعکس، مثال بد، سازگاری ندارد و پیروی از کد را سختتر میکند.
// Good:
const TAX_RATE = 0.1;
const totalPrice = subtotal + (subtotal * TAX_RATE);
// Bad:
const totalPrice = subtotal + (subtotal * 0.1);
اعداد جادویی معنای مقادیر را مبهم می کنند و نگهداری کد را سخت تر می کنند. در مثال خوب، ثابت ها برای نمایش اعداد جادویی، بهبود وضوح کد و قابلیت نگهداری استفاده می شوند.
- اصل مسئولیت واحد:
// Good:
function calculateTotalPrice(quantity, unitPrice) {
return quantity * unitPrice;
}
function formatPrice(price) {
return `$${price.toFixed(2)}`;
}
// Bad:
function calculateAndFormatTotalPrice(quantity, unitPrice) {
const totalPrice = quantity * unitPrice;
return `$${totalPrice.toFixed(2)}`;
}
توابع باید یک مسئولیت واحد برای ترویج قابلیت استفاده مجدد و نگهداری کد داشته باشند. در مثال خوب، هر تابع وظیفه خاصی را با رعایت اصل مسئولیت واحد انجام می دهد. برعکس، مثال بد این اصل را با ترکیب چندین مسئولیت در یک عملکرد نقض می کند.
// Good:
function fetchData(url) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.catch(error => {
console.error('Error fetching data:', error);
throw error;
});
}
// Bad:
function fetchData(url) {
return fetch(url)
.then(response => response.json())
.catch(error => console.error(error));
}
مدیریت صحیح خطا، استحکام کد را بهبود می بخشد و به شناسایی و حل مشکلات به طور موثرتر کمک می کند. در مثال خوب، خطاها با ظرافت مدیریت می شوند و بازخورد معنی داری را به توسعه دهندگان ارائه می دهند. برعکس، مثال بد فاقد مدیریت جامع خطا است که به طور بالقوه منجر به خرابی های بی صدا می شود.
- نظرات و مستندات:
// Good:
// Calculate the total price based on quantity and unit price
function calculateTotalPrice(quantity, unitPrice) {
return quantity * unitPrice;
}
// Bad:
function calculateTotalPrice(quantity, unitPrice) {
// calculate total price
return quantity * unitPrice;
}
نظرات و مستندات درک کد را افزایش داده و همکاری بین توسعه دهندگان را تسهیل می کند. در یک مثال خوب، نظرات واضح، هدف تابع را توصیف میکنند و به درک کد کمک میکنند. برعکس، مثال بد نظرات مبهمی ارائه می دهد که ارزش کمی را اضافه می کند.
// Good:
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// Bad:
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
کد ماژولار قابلیت استفاده مجدد و نگهداری را با سازماندهی عملکرد در واحدهای منسجم ارتقا می دهد. در مثال خوب، توابع به درستی کپسوله شده و صادر می شوند و استفاده مجدد از کد را تسهیل می کنند. برعکس، مثال بد فاقد مدولارسازی است و مدیریت و مقیاس آن را سختتر میکند.
- اصل DRY (خودت را تکرار نکن):
// Good:
const greeting = 'Hello';
function greet(name) {
return `${greeting}, ${name}!`;
}
// Bad:
function greet(name) {
const greeting = 'Hello';
return `${greeting}, ${name}!`;
}
کدهای تکراری خطر خطا را افزایش می دهد و تعمیر و نگهداری را چالش برانگیز می کند. در یک مثال خوب، رشتههای تکراری به یک ثابت استخراج میشوند که به اصل DRY پایبند بوده و قابلیت نگهداری کد را بهبود میبخشد. برعکس، مثال بد به طور اضافی احوالپرسی را در تابع تعریف می کند.
- نام توابع معنی دار:
// Good:
function calculateArea(radius) {
return Math.PI * radius ** 2;
}
// Bad:
function calc(r) {
return Math.PI * r ** 2;
}
نام توابع باید دقیقاً هدف آنها را برای افزایش خوانایی کد منعکس کند. در مثال خوب، نام تابع “calculateArea” به وضوح عملکرد آن را نشان می دهد. برعکس، مثال بد از یک مخفف مرموز (“calc”) استفاده می کند که مشخص نیست عملکرد چه کاری انجام می دهد.
// Good:
function sum(a, b) {
return a + b;
}
module.exports = sum;
// Bad:
function sum(a, b) {
console.log(a + b);
}
نوشتن کد قابل آزمایش، تست خودکار را تسهیل میکند و از قابلیت اطمینان و پایداری کد اطمینان میدهد. در مثال خوب، این تابع برای مقاصد آزمایشی صادر می شود و امکان تنظیم و اجرای آسان تست را فراهم می کند. برعکس، مثال بد حاوی عوارض جانبی (console.log) است که آزمایش رفتار عملکرد را به چالش میکشد.
- استفاده صحیح از ساختارهای داده:
// Good:
const studentGrades = [90, 85, 95, 88];
const averageGrade = studentGrades.reduce((total, grade) => total + grade, 0) / studentGrades.length;
// Bad:
const grade1 = 90;
const grade2 = 85;
const grade3 = 95;
const grade4 = 88;
const averageGrade = (grade1 + grade2 + grade3 + grade4) / 4;
استفاده از ساختارهای داده مناسب خوانایی و نگهداری کد را افزایش می دهد. در مثال خوب، یک آرایه برای ذخیره نمرات دانش آموزان استفاده می شود که امکان دستکاری و محاسبه آسان را فراهم می کند. برعکس، مثال بد به متغیرهای فردی متکی است که منجر به کدهای تکراری و مستعد خطا می شود.
- مدیریت عملیات ناهمزمان:
// Good:
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
return await response.json();
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
// Bad:
function fetchData(url) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.catch(error => {
console.error('Error fetching data:', error);
throw error;
});
}
مدیریت صحیح عملیات ناهمزمان، قابلیت اطمینان و استحکام کد را تضمین می کند. در یک مثال خوب، از syntax async/wait برای ساده کردن کدهای ناهمزمان و رسیدگی به خطاها استفاده میشود. برعکس، مثال بد از وعدههای تودرتو استفاده میکند که منجر به جهنم برگشت به تماس و کاهش خوانایی کد میشود.
// Good:
import { format } from 'date-fns';
// Bad:
const dateFns = require('date-fns');
مدیریت وابستگی موثر ماژولار بودن کد و مقیاس پذیری را ارتقا می دهد. در یک مثال خوب، نحو واردات ES6 برای وارد کردن فقط عملکردهای مورد نیاز از کتابخانه ‘date-fns’ استفاده می شود، که واردات غیر ضروری را کاهش می دهد و عملکرد را بهبود می بخشد. برعکس، مثال بد از دستور CommonJS نیازمند استفاده میکند، که کل ماژول “date-fns” را وارد میکند و به طور بالقوه بسته نرم افزاری را متورم میکند.
- بهینه سازی عملکرد:
// Good:
const sortedNumbers = [5, 2, 8, 1, 9];
sortedNumbers.sort((a, b) => a - b);
// Bad:
const unsortedNumbers = [5, 2, 8, 1, 9];
const sortedNumbers = unsortedNumbers.sort();
بهینه سازی کد برای عملکرد، اجرای کارآمد را تضمین می کند و تجربه کاربر را افزایش می دهد. در یک مثال خوب، متد sort() با یک تابع مقایسه سفارشی برای مرتبسازی اعداد به ترتیب صعودی فراخوانی میشود که در نتیجه عملکرد بهتری در مقایسه با الگوریتم مرتبسازی پیشفرض دارد. برعکس، مثال بد به الگوریتم مرتبسازی پیشفرض متکی است که ممکن است برای آرایههای عددی کارآمدترین نباشد.
- مدیریت صحیح خطا در API های Node.js:
// Good:
app.get('/user/:id', async (req, res) => {
try {
const user = await getUserById(req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
} catch (error) {
console.error('Error fetching user:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Bad:
app.get('/user/:id', async (req, res) => {
const user = await getUserById(req.params.id);
if (!user) {
res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
مدیریت صحیح خطا در API های Node.js برای اطمینان از استحکام و قابلیت اطمینان بسیار مهم است. در مثال خوب، خطاها شناسایی و ثبت می شوند و کدهای وضعیت HTTP مناسب به مشتری بازگردانده می شوند. برعکس، مثال بد در رسیدگی به خطاها ناکام است، که به طور بالقوه منجر به رد وعدههای کنترل نشده و پاسخهای خطای متناقض میشود.
- عملیات کارآمد سیستم فایل:
// Good:
const fs = require('fs').promises;
async function readFile(filePath) {
try {
const data = await fs.readFile(filePath, 'utf-8');
console.log(data);
} catch (error) {
console.error('Error reading file:', error);
}
}
// Bad:
const fs = require('fs');
function readFile(filePath) {
fs.readFile(filePath, 'utf-8', (error, data) => {
if (error) {
console.error('Error reading file:', error);
return;
}
console.log(data);
});
}
استفاده از وعده ها در عملیات سیستم فایل خوانایی کد را افزایش می دهد و مدیریت خطا را ساده می کند. در یک مثال خوب، fs.promises.readFile() برای خواندن یک فایل به صورت ناهمزمان استفاده می شود و خطاها با استفاده از try-catch کنترل می شوند. برعکس، مثال بد از رویکرد مبتنی بر تماس استفاده میکند، که میتواند منجر به جهنم برگشت به تماس و کد کمتر قابل خواندن شود.
- مدیریت کارآمد حافظه:
// Good:
const stream = fs.createReadStream('bigfile.txt');
stream.pipe(response);
// بد:
fs.readFile('bigfile.txt', (error, data) => {
if (error) {
console.error('Error reading file:', error);
return;
}
response.write(data);
});
استفاده از استریم ها برای پردازش فایل های بزرگ در Node.js باعث حفظ حافظه و بهبود عملکرد می شود. در یک مثال خوب، fs.createReadStream() و stream.pipe() برای انتقال موثر داده ها از یک فایل به یک پاسخ HTTP استفاده می شوند. برعکس، مثال بد، قبل از نوشتن آن در پاسخ، کل فایل را در حافظه می خواند، که می تواند منجر به مشکلات حافظه برای فایل های بزرگ شود.
- صادرات و واردات صحیح ماژول:
// Good:
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
// Bad:
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
شیوههای صادرات و وارد کردن ماژولها خوانایی و نگهداری کد را بهبود میبخشد. در مثال خوب، module.exports برای صادرات یک شی حاوی توابع استفاده می شود، در حالی که در مثال بد، صادرات مستقیما استفاده می شود. اگرچه هر دو روش کار می کنند، اما پایبندی به یک قرارداد قوام کد را افزایش می دهد.
- جریان کنترل ناهمزمان:
// Good:
async function processItems(items) {
for (const item of items) {
await processItem(item);
}
}
// Bad:
function processItems(items) {
items.forEach(item => {
processItem(item);
});
}
جریان کنترل ناهمزمان مناسب تضمین می کند که عملیات به صورت متوالی یا همزمان در صورت نیاز اجرا می شوند. در یک مثال خوب، یک تابع async با یک حلقه for… برای پردازش موارد به صورت متوالی، در انتظار هر عملیات استفاده می شود. برعکس، مثال بد از forEach استفاده می کند، که عملیات ناهمزمان را به خوبی مدیریت نمی کند و ممکن است منجر به رفتار غیرمنتظره شود.