Viết hàm sao cho tốt (P1)

Hàm (function hay procedure) trong lập trình giống như các tế bào trong cơ thể chúng ta vậy. Tổ hợp các hàm theo trật tự nhất định sẽ tạo thành các chức năng.

Để các chức năng hoạt động tốt, hiệu năng cao, ta phải viết hàm sao cho tốt. Đây là một câu chuyện dài, vì tùy theo khả năng tư duy, kinh nghiệm của mỗi người mà sẽ có cách viết hàm khác nhau.

Với những bạn ít kinh nghiệm (mới ra trường hoặc đi làm thời gian ngăn), chúng tôi xin chia sẻ một vài nguyên tắc khi viết hàm, các bạn có thể tham khảo.

1. Nguyên tắc vàng: đừng làm nhiều việc trong cùng một hàm 

Nói nôm na, mỗi hàm chỉ nên làm một và chỉ một việc duy nhất. Nếu có 2 việc, hãy tách thành 2 hàm.

Điều này có vô số ích lợi, xin xem một số ví dụ sau.

Anh A nhận nhiệm vụ viết chức năng in ra màn hình thông tin một số n có phải là số nguyên tố.

Anh A viết thế này (các bạn sinh viên thấy quen không ? 😀 )

public void CheckPrime (int n)
{
    if(n < 2)
    {
        Console.WriteLine("is not Prime!");
        return;
    }
    for(int i = 2; i<= Math.Sqrt(n); i++)
    {
        if(n % i == 0)
        {
             Console.WriteLine("is not Prime!");
             return;
        }   
    }
    Console.WriteLine("is Prime!");
    return;
}

Thoạt nhìn không có vấn đề gì lớn. Đến một ngày nọ, khách hàng muốn thêm chức năng kiểm tra xem trong một dãy số (mảng) có bao nhiêu số nguyên tố.

Làm sao đây, thế là anh A lại viết

public void CountPrime(int[] a)
{
    int count = 0;
    bool flag = false;
    for(int id = 0; id< a.Length; id++)
    {
        if(a[id] < 2)        
             continue;        
        flag = true;
        for (int i = 2; i <= Math.Sqrt(a[id]); i++)
        {
             if (a[id] % i == 0)
             {                       
                 flag = false;
                 break;
             }
        }
        if(flag)
            count++;
    }
    Console.WriteLine(count);
}

Vấn đề xuất hiện. Việc kiểm tra một số nguyên có phải là số nguyên tố được lặp đi lặp lại ở cả 2 hàm CheckPrimeCountPrime.

Việc này mất khá nhiều thời gian và công sức cho anh A. Và nếu việc thực hiện hàm CountPrime là do người khác (trong team anh A), ví dụ anh B, anh B có thể khá bực bội với việc không thể tái sử dụng code của anh A vào hàm CountPrime của mình.

Hàm CheckPrime làm 2 việc:

  • Kiểm tra n có phải số nguyên tố
  • In ra màn hình kết quả kiểm tra

Hàm CountPrime không tái sử dụng được code của CheckPrime, vì không có nhu cầu in ra màn hình từng số trong mảng có phải là số nguyên tố, mà chỉ cần đếm số lượng.

Một ví dụ rất nhỏ, những hàm rất đơn giản, để thấy được rằng: nếu một hàm làm nhiều hơn một nhiệm vụ, sẽ rất khó được tái sử dụng cho các công việc sau này, số nhiệm vụ trong hàm càng nhiều, độ khó càng lớn (nếu không muốn nói là không thể).

Trường hợp này giải quyết thế nào? Rất đơn giản, tách việc kiểm ra một số n có phải là số nguyên tố hay không thành một hàm riêng, hàm IsPrime.

private bool IsPrime(int n)
{
    if (n < 2)            
        return false; 
    for (int i = 2; i <= Math.Sqrt(n); i++)
    {
        if (n % i == 0) 
            return false; 
    }
    return true;
}

Vậy CheckPrimeCountPrime được viết lại như sau:

public void CheckPrime(int n)
{
    if (IsPrime(n))
        Console.WriteLine("is Prime!");
    else
        Console.WriteLine("is not Prime!");
}

public void CountPrime(int[] a)
{
    int count = 0;
    for (int id = 0; id < a.Length; id++)
    {
       if (IsPrime(a[id]))
           count++;
    }
    Console.WriteLine(count);
}

Việc chia nhỏ này có một số lợi ích khác. Trong trường hợp việc kiểm tra một số có phải là số nguyên tố được thực hiện sai (có bug), chúng ta không cần phải sửa từng hàm (CheckPrimeCountPrime), mà chỉ cần sửa tại hàm IsPrime là được.

Code trở nên ngắn gọn hơn, dễ đọc dễ hiểu hơn, từ đó giúp giảm thiểu được sai sót không đáng có.

Việc viết Unit test cũng trở nên dễ dàng hơn rất nhiều. Thử tưởng tượng việc mockup cho một mớ lung tung trong một unit test case sẽ gây nhức đầu như thế nào.

Và hằng hà sa số các lợi ích khác khi chúng ta có thể chia tách các hàm đến mức nhỏ nhất có thể, tùy theo kinh nghiệm của mỗi người.

(còn tiếp P2)

Viết một bình luận