0
0
Lập trình
Hưng Nguyễn Xuân 1
Hưng Nguyễn Xuân 1xuanhungptithcm

Hướng Dẫn Refactoring Mã Nguồn: Cách Tối Ưu Hóa Mã Một Cách An Toàn

Đăng vào 1 tuần trước

• 5 phút đọc

Là lập trình viên, một trong những thách thức lớn nhất mà chúng ta thường gặp là quyết định khi nào và làm thế nào để refactor mã nguồn. Nhiều người tin rằng việc refactor mã là cần thiết để nâng cao chất lượng, nhưng thực tế cho thấy, nếu không thực hiện cẩn thận, mã nguồn có thể trở nên khó hiểu và kém hiệu quả hơn.

Trong bài viết này, chúng ta sẽ cùng khám phá các nguyên tắc và chiến lược để thực hiện việc refactor mã một cách hiệu quả và an toàn, giúp bạn tránh được những cạm bẫy phổ biến.

Nguyên Tắc Cơ Bản Trong Việc Refactor Mã

1. Tránh Thay Đổi Phong Cách Lập Trình Quá Đột Ngột

Một sai lầm thường gặp là thay đổi toàn bộ phong cách lập trình khi refactor. Điều này đặc biệt xảy ra với các lập trình viên mới từ các nền tảng khác đến. Hãy nhớ rằng, bất kỳ thay đổi lớn nào cũng có thể gia tăng độ phức tạp không cần thiết.

Ví dụ:

Trước khi refactor:

javascript Copy
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;
}

Refactor sai cách:

javascript Copy
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),
  }))
);

Phiên bản trên có thể gây khó khăn cho nhóm lập trình viên không quen thuộc với thư viện mới như Ramda.

Refactor tốt hơn:

javascript Copy
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 dễ đọc hơn và không làm thay đổi lớn trong cách nhóm làm việc.

2. Không Thêm Abstraction Khi Không Cần Thiết

Gia tăng mức độ trừu tượng hóa trong mã có thể làm cho mã trở nên phức tạp và khó hiểu hơn. Hãy cân nhắc kỹ lưỡng trước khi thêm bất kỳ abstraction nào.

Ví dụ:

Trước khi refactor:

javascript Copy
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;
}

Refactor không cần thiết:

javascript Copy
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();
};

Refactor tốt hơn:

javascript Copy
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);
}

Cách tiếp cận này rõ ràng và dễ hiểu hơn mà không làm tăng độ phức tạp.

3. Giữ Sự Nhất Quán Trong Mã Nguồn

Đừng để cho mã nguồn của bạn trở nên không nhất quán. Điều này có thể gây khó khăn cho việc bảo trì. Nếu một phần của mã sử dụng một thư viện mì gói trong khi phần khác lại sử dụng cách tiếp cận khác, điều này sẽ tạo ra sự mơ hồ và nhiễu loạn.

Ví dụ:

Mã React sử dụng React Query:

javascript Copy
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>;
}

Nhưng một thành viên lại sử dụng Redux Toolkit:

javascript Copy
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>;
}

Refactor thật sự tốt hơn:

javascript Copy
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>;
}

4. Hiểu Rõ Mã Nguồn Trước Khi Refactor

Trước khi chỉnh sửa một đoạn mã, bạn cần hiểu rõ chức năng của nó. Thay đổi mã mà bạn không hiểu có thể dẫn đến lỗi và làm giảm hiệu suất.

Ví dụ:

Trước khi refactor:

javascript Copy
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:

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

Điều này loại bỏ cơ chế lưu trữ, dẫn đến việc gọi API nhiều hơn không cần thiết.

Refactor đúng cách:

javascript Copy
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 sẽ giữ lại cơ chế lưu trữ và tối ưu hóa hơn nữa.

Kết Luận

Refactoring là một phần quan trọng trong phát triển phần mềm, nhưng không thể thực hiện qua loa. Cần có sự hiểu biết rõ ràng về mã nguồn trước khi thực hiện thay đổi. Mục tiêu luôn là cải thiện mã mà không làm biến đổi hành vi của nó.

Khi bạn cảm thấy muốn thay đổi lớn trong một đoạn mã, hãy dừng lại và suy nghĩ. Hiểu mã trước khi thực hiện thay đổi lớn, duy trì sự nhất quán và thực hiện các cải tiến nhỏ nhưng có giá trị. Điều này sẽ giúp cả nhóm làm việc hiệu quả hơn và sản phẩm cuối cùng tốt hơn. Cảm ơn bạn đã đọc. Hy vọng bài viết này sẽ giúp ích cho bạn trong hành trình phát triển phần mềm của mình!
source: viblo

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào