Nhận xét đúng để đặt cho các đối số hàm boolean là Sai false?


19

Từ một số dự án nguồn mở, tôi đã thu thập kiểu mã hóa sau

void someFunction(bool forget);

void ourFunction() {
  someFunction(false /* forget */);
}    

Tôi luôn nghi ngờ về những gì falsecó nghĩa là ở đây. Liệu nó có nghĩa là "quên", hay "quên" đề cập đến tham số tương ứng của nó (như trong trường hợp trên) và "false" có nghĩa là phủ nhận nó?

Phong cách nào được sử dụng thường xuyên nhất và cách tốt nhất (hoặc một số cách tốt hơn) để tránh sự mơ hồ là gì?


38
sử dụng enums (ngay cả khi chỉ có 2 tùy chọn) thay vì bools
Esailija

21
Một số ngôn ngữ hỗ trợ các đối số được đặt tên. Trong các ngôn ngữ như vậy, bạn có thể sử dụngsomeFunction(forget: true);
Brian

3
Tôi cảm thấy bắt buộc phải cung cấp các lập luận của Martin Fowler về Flag Argument (cũng nói về các setters boolean). Nói chung, cố gắng tránh chúng.
FGreg

3
Để tin vào sự rõ ràng, ý kiến ​​có thể nói dối. Do đó luôn luôn tốt hơn để làm cho mã tự tài liệu của bạn, bởi vì một số sẽ thay đổi trueđể falsekhông cần cập nhật các nhận xét. Nếu bạn không thể thay đổi API, thì cách tốt nhất để nhận xét điều này làsomeFunction( false /* true=forget, false=remember */)
Đánh dấu Lakata

1
@Bakuriu - thực ra, có lẽ tôi vẫn có API công khai có hai phương thức riêng biệt ( sortAscendingsortDescending, hoặc tương tự). Bây giờ, bên trong , cả hai có thể gọi cùng một phương thức riêng tư, có thể có loại tham số này. Trên thực tế, nếu ngôn ngữ được hỗ trợ nó, có lẽ những gì tôi muốn vượt qua trong sẽ là một hàm lambda có chứa hướng sắp xếp ...
Clockwork-Muse

Câu trả lời:


33

Trong mã mẫu bạn đã đăng, có vẻ như forgetlà một đối số cờ. (Tôi không thể chắc chắn vì chức năng hoàn toàn là giả thuyết.)

Đối số cờ là một mùi mã. Chúng chỉ ra rằng một chức năng làm nhiều hơn một việc, và một chức năng tốt chỉ nên làm một việc.

Để tránh đối số cờ, chia hàm thành hai hàm giải thích sự khác biệt trong tên hàm.

Đối số cờ

serveIceCream(bool lowFat)

Không có đối số cờ

serveTraditionalIceCream()
serveLowFatIceCream()

chỉnh sửa: Lý tưởng nhất là bạn không cần phải giữ một chức năng với tham số cờ. Có những trường hợp dọc theo dòng mà Fowler gọi là một triển khai rối trong đó việc tách hoàn toàn các hàm tạo ra mã trùng lặp. Tuy nhiên, độ phức tạp chu kỳ của hàm được tham số hóa càng cao thì đối số để loại bỏ nó càng mạnh.


Đây chỉ là một linh cảm, nhưng một tham số có tên forgetnghe có vẻ ghen tị. Tại sao một người gọi sẽ nói với một đối tượng khác để quên một cái gì đó? Có thể có một vấn đề thiết kế lớn hơn.


4
+17, mũ bảo hiểm trên. Cơ thể của serveTraditionalIceCreamserveLowFatIceCreamtrông như thế nào? Tôi có một enum với 14 loại kem.
JohnMark13

13
Đối với các phương thức công khai, quy ước này là tốt, nhưng như JohnMark ám chỉ, nửa còn lại của SRP là "một phương pháp tốt nên là phương pháp duy nhất thực hiện những gì nó làm". Tôi thấy có các biến thể N + 1 của phương pháp này; N công khai không có tham số, tất cả đều gọi một phương thức riêng với tham số bạn đang cố gắng tránh lộ. Tại một số điểm, bạn chỉ cần từ bỏ và phơi bày thông số chết tiệt. Mã mùi không nhất thiết có nghĩa là mã phải được tái cấu trúc; chúng chỉ là những thứ xứng đáng với cái nhìn thứ hai trong đánh giá mã.
KeithS

@Aaron Bởi "tính đố kị" ý bạn là gì?
Geek

27

Theo cách nói của giáo dân:

  • false là một nghĩa đen.
  • bạn đang vượt qua một nghĩa đen false
  • bạn đang nói someFunctionđừng quên
  • bạn đang nói someFunctionrằng tham số quên làfalse
  • bạn đang nói someFunctionđể nhớ

Theo tôi sẽ tốt hơn nếu chức năng như thế này:

void someFunction(bool remember);

bạn có thể gọi nó

void ourFunction() {
  someFunction(true);
} 

hoặc giữ tên cũ nhưng thay đổi chức năng trình bao bọc của bạn thành

void ourFunctionWithRemember() {
  someFunction(false);
} 

CHỈNH SỬA:

Như @Vorac đã nêu, luôn cố gắng sử dụng những từ tích cực. Phủ định kép là khó hiểu.


15
+1 cho ý tưởng luôn cố gắng sử dụng những từ tích cực. Phủ định kép là khó hiểu.
Vorac

1
Đồng ý, ý tưởng tuyệt vời. Nói hệ thống để không làm điều gì đó là khó hiểu. Nếu chấp nhận một tham số boolean, hãy thể hiện nó một cách tích cực.
Brandon

Trong hầu hết các trường hợp, tôi nghĩ bạn cũng muốn cụ thể hơn remember, trừ khi tên hàm làm cho ý nghĩa remember rất rõ ràng. rememberToCleanUp* or *persisthoặc một cái gì đó.
itbruce

14

Tham số có thể được đặt tên tốt; thật khó để nói mà không biết tên của hàm. Tôi cho rằng những nhận xét được viết bởi tác giả ban đầu của hàm, và đó là một lời nhắc nhở về những gì đi falsevào someFunctionphương tiện, nhưng đối với bất cứ ai đến cùng sau đó, đó là một chút không rõ ràng ở cái nhìn đầu tiên.

Sử dụng tên biến tích cực (được đề xuất trong Hoàn thành mã ) có thể là thay đổi đơn giản nhất giúp đoạn mã này dễ đọc hơn, ví dụ:

void someFunction(boolean remember);

sau đó ourFunctiontrở thành:

void ourFunction() {
    someFunction(true /* remember */);
}

Tuy nhiên, việc sử dụng enum làm cho hàm gọi trở nên dễ hiểu hơn, với chi phí của một số mã hỗ trợ:

public enum RememberFoo {
    REMEMBER,
    FORGET
}

...

void someFunction(RememberFoo remember);

...

void ourFunction() {
    someFunction(RememberFoo.REMEMBER);
}

Nếu bạn không thể thay đổi chữ ký someFunctionvì bất kỳ lý do gì, sử dụng biến tạm thời cũng giúp mã dễ đọc hơn, giống như đơn giản hóa các điều kiện bằng cách đưa ra một biến không vì lý do nào khác ngoài việc làm cho mã dễ phân tích hơn bởi con người .

void someFunction(boolean remember);

...

void ourFunction() {
    boolean remember = false;
    someFunction(remember);
}

1
Đặt rememberthành đúng nghĩa là quên (trong ví dụ của bạn someFunction(true /* forget */);)?
Brian

2
Các enumđến nay là giải pháp tốt nhất. Chỉ vì một loại có thể được biểu diễn dưới dạng boolmộtieie, chúng là dạng đồng phân, không có nghĩa là nó phải được biểu diễn như vậy. Lập luận tương tự áp dụng cho stringvà thậm chí int.
Jon Purdy

10

Đổi tên biến để giá trị bool có ý nghĩa.

Điều này tốt hơn một triệu lần so với việc thêm một nhận xét để giải thích các đối số cho một hàm vì tên này không rõ ràng.


3
Điều đó không trả lời câu hỏi. Tất cả mọi thứ đều rõ ràng khi phương thức được định nghĩa và được gọi trong cùng một tệp cách nhau 4 dòng. Nhưng nếu tất cả những gì bạn thấy vào lúc này là người gọi thì sao? Điều gì nếu nó có nhiều booleans? Đôi khi một bình luận nội tuyến đơn giản đi một chặng đường dài.
Brandon

@Brandon Đó không phải là một đối số chống lại việc gọi boolean doNotPersist (hoặc, tốt hơn, Kiên trì). Gọi nó là "quên" mà không nói những gì cần quên là thẳng thắn vô ích. Ồ, và một phương pháp lấy một số booleans làm tùy chọn bốc mùi lên trời cao.
itbruce

5

Tạo một boolean cục bộ với một tên mô tả nhiều hơn và gán giá trị cho nó. Bằng cách đó, ý nghĩa sẽ rõ ràng hơn.

void ourFunction() {
    bool takeAction = false;  /* false means to forget */
    someFunction( takeAction );
}    

Nếu bạn không thể đổi tên biến, thì bình luận nên biểu cảm hơn một chút:

void ourFunction() {
    /* false means that the method should forget what was requested */
    someFunction( false );
}    

1
Lời khuyên chắc chắn là tốt, nhưng tôi không nghĩ rằng nó giải quyết vấn đề mà /* forget */bình luận ở đó để giải quyết, đó là nếu không có khai báo hàm ngay trước mặt bạn, có thể khó nhớ được những gì đang được đặt ra false. (Đó là lý do tại sao tôi nghĩ lời khuyên của @ Esailija để thêm enum là tốt hơn và tại sao tôi thích các ngôn ngữ cho phép các tham số được đặt tên.)
Gort the Robot

@StevenBurnap - cảm ơn! Bạn nói đúng rằng câu trả lời cũ của tôi không đủ rõ ràng để giải quyết câu hỏi của OP. Tôi đã chỉnh sửa nó để làm cho nó rõ ràng hơn.

3

Có một bài viết hay đề cập đến tình huống chính xác này khi đề cập đến API kiểu Qt. Ở đó, nó được gọi là Bẫy thông số Boolean và nó đáng để đọc.

Ý chính của nó là:

  1. Quá tải hàm để bool không cần thiết là tốt hơn
  2. Sử dụng một enum (như Esailija gợi ý) là tốt nhất

2

Đây là một bình luận kỳ quái.

Từ quan điểm của nhà soạn nhạc, someFunction(false /* forget */);thực sự someFunction(false);(bình luận bị tước bỏ). Vì vậy, tất cả dòng đó là cuộc gọi someFunctionvới đối số đầu tiên (và duy nhất) được đặt thành false.

/* forget */chỉ là tên của tham số. Có lẽ không có gì hơn là một lời nhắc nhở nhanh chóng (và bẩn thỉu), mà thực sự không cần phải ở đó. Chỉ cần sử dụng một tên tham số ít mơ hồ hơn và bạn sẽ không cần bình luận gì cả.


1

Một trong những lời khuyên của Clean code là giảm thiểu số lượng bình luận không cần thiết 1 (vì chúng có xu hướng bị thối) và đặt tên cho các hàm và phương thức đúng cách.

Sau đó, tôi sẽ chỉ cần xóa bình luận. Rốt cuộc, các IDE hiện đại (như nhật thực) bật một hộp có mã khi bạn đặt chuột lên hàm. Xem mã nên xóa sự mơ hồ.


1 Nhận xét một số mã phức tạp là ok.


btw Ai đã nói như thế này: "vấn đề tồi tệ nhất của lập trình viên là làm thế nào để đặt tên biến và bù cho một"?
BЈовић

4
Bạn có thể đang tìm kiếm martinfowler.com/bliki/TwoHardThings.html làm nguồn của nó. Tôi đã nghe điều chỉnh thành "Chỉ có hai điều khó khăn trong Khoa học máy tính: vô hiệu hóa bộ đệm, đặt tên và tắt một lỗi."

1

Để tin vào sự rõ ràng, ý kiến ​​có thể nói dối. Do đó luôn luôn là tốt hơn để làm cho mã tự tài liệu của bạn mà không cần đến ý kiến để giải thích, bởi vì một số người (có lẽ bạn) sẽ thay đổi trueđể falsekhông cần cập nhật các nhận xét.

Nếu bạn không thể thay đổi API, thì tôi sử dụng 2 tùy chọn

  • Thay đổi nhận xét để nó luôn luôn đúng, bất kể mã. Nếu bạn chỉ gọi điều này một lần, đây là một giải pháp tốt, vì nó giữ tài liệu cục bộ.
     someF rối (false / * true = quên, false = nhớ * /); `
  • Sử dụng #defines, đặc biệt nếu bạn gọi nó nhiều lần.
     #define FORGET đúng
     #define NHỚ sai
     một số chức năng (NHỚ);

1

Tôi thích câu trả lời về việc làm cho nhận xét luôn luôn đúng , nhưng trong khi tốt, tôi nghĩ rằng nó bỏ lỡ vấn đề cơ bản với mã này - nó được gọi bằng một nghĩa đen.

Bạn nên tránh sử dụng chữ khi gọi phương thức. Biến cục bộ, tham số tùy chọn, tham số được đặt tên, enums - cách tốt nhất bạn tránh chúng sẽ phụ thuộc vào ngôn ngữ và những gì có sẵn, nhưng hãy cố gắng tránh chúng. Văn học có giá trị, nhưng chúng không có ý nghĩa.


-1

Trong C #, tôi đã sử dụng các tham số được đặt tên để làm cho điều này rõ ràng hơn

someFunction(forget: false);

Hoặc enum:

enum Memory { Remember, Forget };

someFunction(Memory.Forget);

Hoặc quá tải:

someFunctionForget();

Hoặc đa hình`:

var foo = new Elephantine();

foo.someFunction();

-2

Việc đặt tên phải luôn luôn giải quyết sự mơ hồ cho booleans. Tôi luôn đặt tên boolean một cái gì đó như 'is This' hoặc 'ShouldDoThat', ví dụ:

void printTree(Tree tree, bool shouldPrintPretty){ ... }

vân vân Nhưng khi bạn đang tham chiếu mã của người khác, tốt nhất chỉ nên để lại nhận xét khi truyền giá trị.


Làm thế nào để trả lời câu hỏi này?
gnat

@gnat Tôi đang trả lời câu hỏi của anh ấy về việc giải quyết sự mơ hồ với các tham số boolean. Có lẽ tôi đọc nhầm câu hỏi của anh.
dchhetri
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.