- vừa được xem lúc

Refactoring Code: Làm Thế Nào Để Tối Ưu Mã Nguồn Mà Không Gây Ra Thảm Họa?

0 0 9

Người đăng: Phạm Tiến Thành Công

Theo Viblo Asia

Là một lập trình viên, Một trong những tình huống phổ biến mình thường gặp là có nhiều người rất tin rằng mã nguồn hiện tại cần phải được refactor lại mạnh mẽ. Tuy nhiên, thực tế cho thấy, hầu hết những mã nguồn đã được refactor đó thường trở nên khó hiểu hơn, khó bảo trì hơn, chạy chậm hơn và thậm chí còn nhiều lỗi hơn.

Đừng hiểu sai, refactoring là một việc cần thiết và quan trọng để duy trì chất lượng mã nguồn. Vấn đề chỉ nảy sinh khi việc refactor được thực hiện sai cách, và rất dễ rơi vào tình huống làm cho mọi thứ tệ hơn khi bạn đang cố gắng cải thiện nó.

Hãy cùng nhau tìm hiểu điều gì tạo nên một lần refactor tốt và cách tránh các cạm bẫy khiến mã nguồn trở nên kém chất lượng.

Những Yếu Tố Chính Trong Refactoring

1. Thay Đổi Phong Cách Lập Trình Quá Mức

Một lỗi thường gặp là khi lập trình viên thay đổi toàn bộ phong cách lập trình trong quá trình refactor. Điều này thường xảy ra khi người lập trình đến từ một nền tảng khác hoặc có quan điểm mạnh về một phong cách lập trình nhất định.

Ví dụ:

Trước khi refactor:

function processUsers(users) { const result = []; for (let i = 0; i < users.length; i++) { if (users[i].age >= 18) { result.push({ name: users[i].name.toUpperCase(), age: users[i].age, isAdult: true, }); } } return result;
}

Sau khi refactor không đúng cách:

import * as R from 'ramda'; const processUsers = R.pipe( R.filter(R.propSatisfies(R.gte(R.__, 18), 'age')), R.map(R.applySpec({ name: R.pipe(R.prop('name'), R.toUpper), age: R.prop('age'), isAdult: R.always(true), }))
);

Mặc dù đoạn mã này có thể hấp dẫn đối với những ai thích lập trình hàm, nhưng nó lại làm tăng độ phức tạp không cần thiết, đặc biệt là khi nhóm của bạn không quen thuộc với thư viện mới như Ramda.

Refactor tốt hơn:

function processUsers(users) { return users .filter(user => user.age >= 18) .map(user => ({ name: user.name.toUpperCase(), age: user.age, isAdult: true, }));
}

Phiên bản này gọn gàng hơn, dễ đọc hơn và không đòi hỏi sự thay đổi lớn trong cách tiếp cận của nhóm.

2. Trừu Tượng Hóa Không Cần Thiết

Thêm abstraction vào mã nguồn có thể làm cho mọi thứ trở nên phức tạp và khó bảo trì, đặc biệt khi nó không thực sự cần thiết.

Ví dụ:

Trước khi refactor:

function processUsers(users) { const result = []; for (let i = 0; i < users.length; i++) { if (users[i].age >= 18) { result.push({ name: users[i].name.toUpperCase(), age: users[i].age, isAdult: true, }); } } return result;
}

Sau khi refactor không cần thiết:

class UserProcessor { constructor(users) { this.users = users; } process() { return this.filterAdults().formatUsers(); } filterAdults() { this.users = this.users.filter(user => user.age >= 18); return this; } formatUsers() { return this.users.map(user => ({ name: this.formatName(user.name), age: user.age, isAdult: true, })); } formatName(name) { return name.toUpperCase(); }
} const processUsers = (users) => { return new UserProcessor(users).process();
};

Đoạn mã này tuy có vẻ "hướng đối tượng" hơn, nhưng thực tế lại phức tạp hơn và khó hiểu hơn.

Refactor tốt hơn:

const isAdult = user => user.age >= 18;
const formatUser = user => ({ name: user.name.toUpperCase(), age: user.age, isAdult: true,
}); function processUsers(users) { return users.filter(isAdult).map(formatUser);
}

Đây là một cách tiếp cận gọn gàng, dễ hiểu và không làm tăng độ phức tạp không cần thiết.

3. Tạo Ra Sự Không Nhất Quán

Đôi khi, lập trình viên cập nhật một phần của mã để nó hoạt động khác hoàn toàn với phần còn lại. Điều này có thể gây bối rối và làm giảm hiệu suất làm việc của cả đội.

Ví dụ:

Nếu toàn bộ ứng dụng React sử dụng React Query để lấy dữ liệu:

import { useQuery } from 'react-query'; function UserProfile({ userId }) { const { data: user, isLoading } = useQuery(['user', userId], fetchUser); if (isLoading) return <div>Loading...</div>; return <div>{user.name}</div>;
}

Thế nhưng, một thành viên lại quyết định sử dụng Redux Toolkit cho một thành phần duy nhất:

import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchPosts } from './postsSlice'; function PostList() { const dispatch = useDispatch(); const { posts, status } = useSelector(state => state.posts); useEffect(() => { dispatch(fetchPosts()); }, [dispatch]); if (status === 'loading') return <div>Loading...</div>; return <div>{posts.map(post => <div key={post.id}>{post.title}</div>)}</div>;
}

Điều này làm cho mã nguồn không nhất quán và khó duy trì.

Refactor tốt hơn:

import { useQuery } from 'react-query'; function PostList() { const { data: posts, isLoading } = useQuery('posts', fetchPosts); if (isLoading) return <div>Loading...</div>; return <div>{posts.map(post => <div key={post.id}>{post.title}</div>)}</div>;
}

Giữ mã nhất quán là điều rất quan trọng để giúp cả đội dễ dàng làm việc và bảo trì mã nguồn.

4. Không Hiểu Rõ Mã Trước Khi Refactor

Refactor một đoạn mã mà bạn chưa hiểu rõ có thể dẫn đến việc tạo ra lỗi mới hoặc làm giảm hiệu suất. Điều quan trọng là bạn phải nắm rõ mã nguồn trước khi thực hiện bất kỳ thay đổi lớn nào.

Ví dụ:

Trước khi refactor:

function fetchUserData(userId) { const cachedData = localStorage.getItem(`user_${userId}`); if (cachedData) { return JSON.parse(cachedData); } return api.fetchUser(userId).then(userData => { localStorage.setItem(`user_${userId}`, JSON.stringify(userData)); return userData; });
}

Refactor không đúng cách:

function fetchUserData(userId) { return api.fetchUser(userId);
}

Phiên bản này loại bỏ cơ chế caching, làm tăng số lượng gọi API và có thể làm giảm hiệu suất của ứng dụng.

Refactor đúng cách:

async function fetchUserData(userId) { const cachedData = await cacheManager.get(`user_${userId}`); if (cachedData) { return cachedData; } const userData = await api.fetchUser(userId); await cacheManager.set(`user_${userId}`, userData, { expiresIn: '1h' }); return userData;
}

Phiên bản này giữ lại cơ chế caching và có thể cải thiện bằng cách sử dụng một hệ thống cache phức tạp hơn.

Kết Luận

Refactoring là một phần không thể thiếu trong phát triển phần mềm, nhưng nó cần được thực hiện một cách thận trọng và phải hiểu rõ ngữ cảnh của mã nguồn. Mục tiêu là cải thiện mã mà không làm thay đổi hành vi bên ngoài của nó.

Lần tới khi bạn cảm thấy muốn thực hiện thay đổi lớn cho một đoạn mã, hãy dừng lại và suy nghĩ cẩn thận. Hiểu rõ mã trước khi thay đổi, giữ mã nhất quán và thực hiện những cải tiến nhỏ mà có giá trị lớn. Đồng đội của bạn sẽ cảm ơn bạn vì điều đó!

Nguồn: Link bài viết gốc

Cảm ơn anh em đã đọc bài viết của mình. Hi vọng bài viết sẽ giúp ích cho anh em.

Anh em hãy theo giõi mình để có thêm nhiều bài viết hay và bổ ích nhé !

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 525

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 433

- vừa được xem lúc

Một số phương thức với object trong Javascript

Trong Javascript có hỗ trợ các loại dữ liệu cơ bản là giống với hầu hết những ngôn ngữ lập trình khác. Bài viết này mình sẽ giới thiệu về Object và một số phương thức thường dùng với nó.

0 0 153

- vừa được xem lúc

Tìm hiểu về thư viện axios

Giới thiệu. Axios là gì? Axios là một thư viện HTTP Client dựa trên Promise.

0 0 145

- vừa được xem lúc

Imports và Exports trong JavaScript ES6

. Giới thiệu. ES6 cung cấp cho chúng ta import (nhập), export (xuất) các functions, biến từ module này sang module khác và sử dụng nó trong các file khác.

0 0 110

- vừa được xem lúc

Bài toán đọc số thành chữ (phần 2) - Hoàn chỉnh chương trình dưới 100 dòng code

Tiếp tục bài viết còn dang dở ở phần trước Phân tích bài toán đọc số thành chữ (phần 1) - Phân tích đề và những mảnh ghép đầu tiên. Bạn nào chưa đọc thì có thể xem ở link trên trước nhé.

0 0 245