Clean code series: Part 3 - Functions

Functions là gì ?

Nếu coi chương trình như 1 công trình kiến trúc thì functions là những viên gạch để xây lên công trình đó. Việc và để có một công trình tốt thì phải làm ra những viên gạch tốt.

Và hôm nay chúng ta cùng tìm hiểm xem làm như thế nào để tạo ra 1 chương trình tốt.

Small!!!

  • Nguyên tắc đầu tiên của hàm là chúng phải nhỏ. Nguyên tắc thứ hai là chúng phải nhỏ hơn nữa.

Mỗi hàm kể một câu chuyện và dẫn bạn đến hàm tiếp theo hấp dẫn hơn. Mỗi hàm sẽ được đặt cho 1 cái tên thật đẹp để chỉ cần nhìn vào tên hàm là bạn đã biết được nó làm việc gì khi chưa cần phải nhìn vào đống code bên trong nó. Việc đọc hiểu nó sẽ tiết kiệm cho bạn rất nhiều thời gian đấy.

  • Các khối lệnh bên trong câu lệnh if, else, while,…phải dài một dòng.

Và dòng đó nên là một lời gọi hàm. Điều này cũng có nghĩa các hàm không nên được thiết kế lớn để chứa các cấu trúc lồng nhau. Do đó, lời gọi hàm không nên thụt lề quá mức hai.

Ex: dưới đây là 1 điển hình ko nên

public function calculator() {  
   if ($a > 1) {
      if ($b > 2) {
         $a = $a + $b;
      }
   }
}

Do one thing

"HÀM CHỈ NÊN THỰC HIỆN MỘT VIỆC. CHÚNG NÊN LÀM TỐT VIỆC ĐÓ, VÀ CHỈ LÀM DUY NHẤT VIỆC ĐÓ"

  • Lý do : viết các hàm là để phân tích một khái niệm lớn thành các khái niệm nhỏ hơn (nói cách khác, là phân tích tên hàm thành các tên ở mức độ thấp hơn). Một cách khác để biết hàm đang làm nhiều hơn “một việc” là khi bạn có thể trích xuất một hàm khác từ nó, nhưng với một cái tên khác so với chức năng của nó ở trong hàm.

One level of abstraction per function

  • Để đảm bảo các hàm của chúng ta đang thực hiện “một việc”, chúng ta cần chắc chắn rằng các câu lệnh trong hàm của chúng ta đều ở cùng cấp độ trừu tượng. Việc trộn lẫn nhiều cấp độ trong cùng 1 function có thể tạo ra những sự nhầm lẫn cho người đọc.

Đây là 1 ví dụ không nên khi xuất hiên 2 cấp độ trừu tượng.

public function index()  
{
    $postIds = request('postIds');
    $posts = $this->postRepository->getList($postIds);
    return $this->response($posts);
}

Đây là 1 ví dụ sau khi viết clean code.

public function index()  
{
    $postIds = request('postIds');
    $posts = $this->getPostsFromIds($postIds);
    return $this->response($posts);
}
public function getPostsFromIds($postIds)  
{
    $posts = $this->postRepository->getList($postIds);
    return $posts;
}
  • Reading Code from Top to Bottom: The Step down Rule: khi chúng tôi xem xét một danh sách các khai báo hàm, mức độ trừu tượng của chúng phải được giảm dần. Tôi gọi đó là nguyên tắc Stepdown (tạm dịch: nguyên tắc ruộng bậc thang).
public function index()  
{
    $this->subFunctionLevelA1();
    $this->subFunctionLevelB1();
    $this->subFunctionLevelC1();
}
public function subFunctionLevelA1()  
{
    $this->subFunctionLevelA11();
    $this->subFunctionLevelA12();
}

public function subFunctionLevelA11() {}  
public function subFunctionLevelA12() {}

public function subFunctionLevelB1() {};  
public function subFunctionLevelC1() {};

Việc chia thành từng khối khi nhìn vào những funtion liên quan đến nhau cùng trong 1 khung hình giúp cho việc đọc hiểu nhanh chóng hơn.

Don't repeat yourself

Không dễ dàng để phát hiện ra sự trùng lặp này vì cả bốn trường hợp code được trộn lẫn với code khác, và sự sao chép là không thống nhất. Tuy nhiên, việc trùng lặp code như vậy là một vấn đề, vì nó làm code của bạn phình to ra và khi cần sửa đổi, bạn sẽ phải sửa đổi "n" lần. Điều đó cũng đồng nghĩa với việc nguy cơ xuất hiện lỗi là "n" lần.

Sự trùng lặp có lẽ là gốc rễ của mọi tội lỗi trong lập trình. Nhiều nguyên tắc và kinh nghiệm đã được tạo ra cho mục đích kiểm soát hoặc loại bỏ nó. Lập trình cấu trúc, lập trình hướng đối tượng (OOP), lập trình hướng khía cạnh (Aspect Oriented Programming – AOP), lập trình hướng thành phần (Component Oriented Programming – COP), tất cả chúng đều có chiến lược để loại bỏ code trùng lặp. Nó chứng minh rằng kể từ khi chương trình con được phát minh, các sáng kiến trong ngành công nghiệp phát triển phần mềm đều nhắm đến việc loại bỏ những đoạn code trùng lặp ra khỏi mã nguồn.

Use Descriptive Names

  • Hãy nhớ đến nguyên tắc của Ward: “Bạn biết bạn đang làm việc cùng code sạch là khi việc đọc code hóa ra yomost hơn những gì bạn mong đợi”. Một nửa chặng đường để đạt được nguyên tắc đó là chọn được một cái tên “xịn” cho những hàm làm “một việc”. Hàm càng nhỏ và càng cô đặc thì càng dễ chọn tên mô tả cho nó hơn.
  • Đừng ngại đặt tên dài
  • Đừng ngại dành thời gian cho việc chọn tên
  • Chọn một cái tên có tính mô tả tốt sẽ giúp bạn vẽ lại thiết kế của mô-đun đó vào não, và việc cải thiện nó sẽ đơn giản hơn.

Function Arguments

  • Số lượng đối số lý tưởng cho một hàm là không (niladic), tiếp đến là một (monadic), sau đó là hai (dyadic). Nên tránh trường hợp ba đối số (triadic) nếu có thể. Các hàm có nhiều hơn ba đối số (polyadic) chỉ cần thiết trong các trường hợp đặc biệt, và sau đó nên hạn chế sử dụng chúng đến mức thấp nhất.

  • Hàm có hai đối số sẽ khó hiểu hơn hàm có một đối số. Ví dụ:

writeField(name)  

sẽ dễ hiểu hơn

writeField(output-Stream, name)  

Mặc dù ý nghĩa của cả hai đều như nhau, hàm thứ nhất dễ hiểu khi lần đầu nhìn vào. Nhưng hàm thứ hai yêu cầu bạn phải dừng lại, cho đến khi bạn học được cách bỏ qua tham số đầu tiên. Và, dĩ nhiên, có một vấn đề khi bạn bỏ qua đoạn code nào đó, thì khả năng đoạn code đó chứa lỗi là rất cao.

Tất nhiên luôn có những lúc hai đối số sẽ hợp lý hơn một đối số.
Ví dụ:

Point p = new Point(0,0);  

là hoàn toàn hợp lý khi bạn đang code về tọa độ mặt phẳng. Chúng tôi sẽ cảm thấy bối rối khi thấy

new Point(0);  

trong trường hợp này.

  • Đối số đối tượng Khi một hàm có vẻ cần nhiều hơn hai hoặc ba đối số, có khả năng một số đối số đó phải được bao bọc thành một lớp riêng của chúng. Ví dụ, hãy xem xét sự khác biệt giữa hai khai báo sau đây:
Circle makeCircle(double x, double y, double radius);  
Circle makeCircle(Point center, double radius);  

Giảm số lượng các đối số bằng cách tạo ra các đối tượng có vẻ như gian lận, nhưng không phải. Khi các nhóm biến được chuyển đổi cùng nhau, như cách x và y ở ví dụ trên, chúng có khả năng là một phần của một khái niệm xứng đáng có tên riêng.

Have No Side Effects.

Tác dụng phụ (hay hiệu ứng lề) là một sự lừa dối. Hàm của bạn được hy vọng sẽ làm một việc, nhưng nó cũng làm những việc khác mà bạn không thấy. Đôi khi nó bất ngờ làm thay đổi giá trị biến của lớp của nó. Hoặc nó sẽ biến chúng thành các tham số được truyền vào hàm, hoặc các hàm toàn cục. Trong cả hai trường hợp, chúng tạo ra các sai lầm và làm sai kết quả.

How do you write functions like this?

Viết phần mềm cũng giống như viết các thể loại khác. Khi bạn viết một bài báo hay một văn kiện, bạn sẽ suy nghĩ trước, sau đó bạn nhào nặn nó cho đến khi nó trở nên mạch lạc, trơn tru. Các bản thảo ban đầu có thể vụng về và rời rạc, vì vậy bạn vứt nó vào sọt rác và tái cơ cấu nó, tinh chỉnh nó cho đến khi nó được đọc theo cách mà bạn muốn.

Hy vọng bài viết sẽ giúp ích cho công việc của bạn. Xin chào và hẹn gặp lại!

Related article