Làm thế nào để đặt tên cho một phương thức vừa thực hiện một nhiệm vụ vừa trả về một boolean dưới dạng trạng thái?


33

Nếu có một phương pháp

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

nó nên được gọi là IsStuffDone()?

Cả hai tên có thể bị người dùng hiểu sai: Nếu tên đó là DoStuff()lý do tại sao nó trả về một boolean? Nếu tên là IsStuffDone()không rõ liệu phương thức thực hiện một nhiệm vụ hay chỉ kiểm tra kết quả của nó.

Có một quy ước cho trường hợp này? Hoặc một cách tiếp cận khác, vì cách này được coi là thiếu sót? Ví dụ: trong các ngôn ngữ có tham số đầu ra, như C #, một biến trạng thái boolean có thể được truyền cho phương thức dưới dạng một và kiểu trả về của phương thức sẽ là void.

EDIT: Trong xử lý ngoại lệ vấn đề cụ thể của tôi không thể ủy thác trực tiếp cho người gọi, vì phương thức này là một phần của việc triển khai giao diện. Do đó, người gọi không thể bị buộc tội đối phó với tất cả các ngoại lệ của các triển khai khác nhau. Nó không quen thuộc với những ngoại lệ đó. Tuy nhiên, người gọi có thể xử lý một ngoại lệ tùy chỉnh StuffHasNotBeenDoneForSomeReasonExceptionnhư được đề xuất trong câu trả lời và nhận xét của npinti .


2
Nó phụ thuộc vào việc sử dụng chức năng và những thứ đang được thực hiện. Nếu cần thiết, các công cụ cần phải được thực thi chính xác, thì tôi sẽ xem xét phương pháp này là thiếu sót, bởi vì người sử dụng hàm có thể bỏ lỡ cờ boolean và cũng thiếu thông tin do ngoại lệ cung cấp.
Benni

7
Hầu hết thời gian, tôi gọi nó là "hỏng" vì trả lại booleanthay vì gói hoặc vượt qua ngoại lệ gần như luôn luôn sai.
maaartinus

2
Loại xử lý ngoại lệ này hiếm khi là một ý tưởng tốt. Chỉ nắm bắt các ngoại lệ cụ thể mà bạn mong đợi, không phải tất cả chúng. Và nếu có thể tránh ném nó ở nơi đầu tiên, loại bỏ sự cần thiết phải bắt nó.
CodeInChaos

2
Ừm ... BadlyDesignedMethodInSeriousNeedOfRefactoring? Và để trả lời câu hỏi của bạn về các trường hợp ngoại lệ - Tôi sẽ để người gọi xử lý chúng hoặc bắt chúng và sau đó ném một ngoại lệ tùy chỉnh có nghĩa là "phương pháp này không thực hiện công việc của nó". Chia sẻ và tận hưởng.
Bob Jarvis - Tái lập Monica

5
Đối với tất cả những người đang nói: chỉ cần ném (hoặc bỏ qua) một ngoại lệ, bạn đang đưa ra những giả định vô căn cứ về cách sử dụng mã này. Một kịch bản có thể xảy ra là có một vấn đề cần giải quyết, với các phương pháp giải pháp heuristic khác nhau để giải quyết các lớp con lớn hơn của vấn đề với chi phí ngày càng tăng; nó sẽ có ý nghĩa để viết một cái gì đó như if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);. Làm tương tự với các ngoại lệ (tùy chỉnh?) Sẽ phức tạp hơn vô ích.
Marc van Leeuwen

Câu trả lời:


68

Trong .NET, bạn thường có các cặp phương thức trong đó một trong số chúng có thể đưa ra một ngoại lệ ( DoStuff) và phương thức kia trả về trạng thái Boolean và, khi thực hiện thành công, kết quả thực tế thông qua tham số out ( TryDoStuff).

(Microsoft gọi đây là "Mô hình phân tích thử" , vì có lẽ ví dụ nổi bật nhất cho nó là các TryParsephương thức của các kiểu nguyên thủy khác nhau.)

Nếu Trytiền tố không phổ biến trong ngôn ngữ của bạn, thì có lẽ bạn không nên sử dụng nó.


3
+1 Đây là cách tôi đã thấy loại điều này được thực hiện ở nơi khác. if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");theo ý kiến ​​của tôi là một thành ngữ khá chuẩn và trực quan.
Karl Nicoll

12
Cần lưu ý rằng TryDoStuffcác phương thức được coi là thất bại sạch sẽ và không có tác dụng phụ khi chúng trả về sai.
Trillian

FYI, cũng có Removecác phương thức ví dụ trong .NET framework sẽ loại bỏ một phần tử khỏi cấu trúc dữ liệu (ví dụ Dictionary) và trả về a bool. Người tiêu dùng API có thể quyết định tiêu thụ hoặc bỏ qua nó. Tuy nhiên, lỗi được báo cáo là ngoại lệ.
Omer Iqbal

18

Điều gì nếu bạn chỉ đơn giản là ném ngoại lệ cho mã gọi?

Bằng cách này, bạn đang ủy thác việc xử lý ngoại lệ cho những người đã từng sử dụng mã của bạn. Điều gì sẽ xảy ra nếu trong tương lai bạn muốn làm như sau:

  • Nếu không có ngoại lệ được ném, hãy hành động A
  • Nếu (ví dụ) a FileNotFoundExceptionbị ném, hãy thực hiện hành động B
  • Nếu có bất kỳ ngoại lệ nào khác được ném, hãy hành động C

Nếu bạn ném lại ngoại lệ của mình, thay đổi ở trên chỉ đơn giản là đòi hỏi phải thêm một catchkhối bổ sung . Nếu bạn để nguyên như vậy, bạn sẽ cần thay đổi phương thức và địa điểm mà phương thức được gọi, tùy thuộc vào độ phức tạp của dự án, có thể ở nhiều vị trí.


1
Điều gì xảy ra nếu ngoại lệ là loại không thể ủy thác và nên được xử lý nội bộ?
Limbo Exile

2
@LimboExile: Tôi nghĩ rằng bạn vẫn có thể giải quyết nội bộ và sau đó, có thể đưa ra một ngoại lệ khác, có thể là của riêng bạn. Điều này sẽ minh họa rằng điều gì đó không nên xảy ra đã xảy ra, nhưng đồng thời, bạn đã không phơi bày những gì thực sự diễn ra dưới mui xe, mà tôi cho rằng đó là lý do tại sao bạn muốn giải quyết ngoại lệ trong nội bộ.
npinti

6
Có lẽ nên nhớ rằng việc ném một ngoại lệ nên dành cho một trường hợp ngoại lệ . Nếu nó là phổ biến hoặc bình thường cho DoStuffthất bại, ném một ngoại lệ cho trường hợp thất bại sẽ giống như kiểm soát dòng chương trình với các ngoại lệ là xấu. Ngoại lệ cũng có một chi phí hiệu suất vốn có. Nếu DoStuffthất bại là trường hợp không phổ biến do lỗi, thì ngoại lệ chắc chắn là cách để đi như @npinti gợi ý.
Karl Nicoll

1
@KarlNicoll Cho dù một cái gì đó là "đặc biệt" là hoàn toàn chủ quan. Các tiêu chí chính phải là liệu bạn có muốn chương trình gặp sự cố hay không nếu không ai làm gì về lỗi và liệu bạn có muốn khả năng xảy ra lỗi trong loại của hàm không. Ngoài ra, thực tế không phải là ngoại lệ đắt tiền; nó phụ thuộc vào ngôn ngữ và cách bạn ném chúng. Các ngoại lệ là rẻ trong Python và nếu bạn loại bỏ thông tin theo dõi ngăn xếp thì chúng cũng có thể rẻ trong Java. Ngay cả khi có một chi phí hiệu suất, đó là tối ưu hóa sớm để lo lắng về nó cho đến khi bạn định hình.
Doval

1
@Doval - Tôi đồng ý rằng đó là sự chủ quan, đó là lý do tại sao OP không nên giảm giá hoàn toàn cho câu trả lời của npinti, vì đó cũng có thể là cách hành động tốt nhất. Quan điểm của tôi là ném ngoại lệ không phải luôn luôn là con đường tốt nhất để đi. Có rất nhiều trường hợp thất bại không biện minh cho việc ném ngoại lệ và có khả năng làm hỏng ứng dụng. Ví dụ: nếu DoStuff()phương thức của OP dọn sạch sau khi mã bên trong ném ra một ngoại lệ và Điều kiện hậu của phương thức vẫn đúng, mã trả về có thể là một lựa chọn tốt hơn vì lỗi đã được xử lý.
Karl Nicoll

7

DoStuff() là đủ và giá trị trả về của hàm phải được ghi lại và bạn không cần đề cập đến nó trong tên hàm, tìm kiếm nhiều API có sẵn:

PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

C-sắc nét

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )

6

Trong Java, API Collection xác định một phương thức add trả về boolean. Về cơ bản nó trả về việc add đã thay đổi bộ sưu tập. Vì vậy, đối với Listnó thường sẽ trả về true, vì mục này đã được thêm vào, trong khi đối với Setnó, nó có thể trả về false, nếu mục đó đã ở đó, vì Sets cho phép mỗi mục duy nhất nhiều nhất một lần.

Điều đó nói rằng, nếu bạn thêm một mục không được bộ sưu tập cho phép, ví dụ: giá trị null, thì add sẽ ném NullPointerExceptionthay vì trả về false.

Khi bạn áp dụng logic tương tự cho trường hợp của bạn, tại sao bạn cần trả về một boolean? Nếu đó là để che giấu một ngoại lệ, đừng. Chỉ cần ném ngoại lệ. Bằng cách đó bạn có thể thấy những gì đã sai. Nếu bạn cần một boolean, vì nó có thể chưa được thực hiện, vì một số lý do khác ngoài một ngoại lệ, hãy đặt tên cho phương thức DoStuff(), vì điều đó thể hiện hành vi. Nó làm công cụ.


1

Có thể thất bại vì những lý do khác nhau, Ngoại lệ, điều kiện bình thường, vì vậy sẽ sử dụng điều này:

boolean doStuff() - returns true when successful

Quan trọng là ghi lại ý nghĩa của boolean.


1

Tôi thường có các phương thức trả về một OperationResult(hoặc OperationResult<T>) và đặt thuộc IsSuccesstính một OperationResultcách thích hợp. Nếu phương thức 'thông thường' là void thì trả về OperationResult, nếu phương thức 'thông thường' trả về một đối tượng thì trả về OperationResult<T>và đặt thuộc Itemtính trên một OperationResultcách thích hợp. Tôi cũng đề nghị có các phương pháp OperationResultnhư .Failed(string reason).Succeeded(T item). OperationResultnhanh chóng trở thành một loại quen thuộc trong hệ thống của bạn và các nhà phát triển biết cách xử lý nó.


0

Nó thực sự phụ thuộc vào ngôn ngữ mà bạn đang sử dụng. Giống như đối với một số ngôn ngữ, có những quy ước và giao thức thực sự không tồn tại trong nhiều ngôn ngữ hoặc bất kỳ ngôn ngữ nào.

Ví dụ: trong ngôn ngữ Objective-C và tên phương thức SDK của iOS / Mac OS X thường đi theo dòng:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

Như bạn có thể thấy, có một phương thức có tên là viewDidLoad. Tôi thích sử dụng kiểu đặt tên này bất cứ khi nào tôi tạo các ứng dụng của riêng mình. Điều này có thể được sử dụng để kiểm tra nếu một cái gì đó được cho là xảy ra như bạn nói. Tôi tin rằng bạn đang suy nghĩ quá sâu về những gì bạn đặt tên cho phương pháp của mình. Hầu hết các phương thức trả về trạng thái của những gì chúng làm bất kể, lấy ví dụ:

Trong PHP, mysql_connect () sẽ trả về false nếu kết nối không thành công. Nó không nói nó sẽ làm, nhưng nó và tài liệu nói rằng nó làm như vậy nó không thể bị hiểu sai cho lập trình viên.


À, nhưng viewDidLoad có nhiều thông báo gọi lại. Người dùng không gọi nó để tải chế độ xem. Thay vào đó, nó được gọi sau khi chế độ xem được tải. Tương tự như vậy: isVisible không đặt mức độ hiển thị, thay vào đó nó chỉ trả về giá trị hiện tại. Nó không làm gì cả.
lilbyrdie

0

Tôi muốn đề nghị sử dụng tiền tố 'Thử'. Đây là một mô hình nổi tiếng cho các tình huống mà phương pháp có thể thành công hay không. Mẫu đặt tên này đã được Microsoft sử dụng trong .NET Framework (ví dụ: TryPudeInt).

Nhưng, có lẽ đây không phải là về đặt tên. Đó là về cách bạn thiết kế các tham số đầu vào / đầu ra của phương thức.

Ý tưởng của tôi là nếu một phương thức có giá trị trả về, thì mục đích của việc gọi phương thức đó là để có được giá trị trả về đó. Vì vậy, bạn nên tránh sử dụng loại trả về để chỉ trạng thái của một hoạt động.

Trong C #, tôi thích sử dụng tham số out. Sử dụng một ra tôi đang đánh dấu tham số là một tham số phụ. Đặc biệt là C # 6 sẽ hỗ trợ khai báo biến nội tuyến.

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.

0

Tôi sẽ chọn một tên dựa trên vai trò chính của phương thức. Thực tế, phương thức đó trả về giá trị boolean không bắt buộc tiền tố 'Is'. Chúng ta hãy có một số mã Java, sử dụng tiền tố 'Is' khá mở rộng. Hãy nhìn vào java.io.File.delete(). Nó trả về boolean, nhưng nó không được gọi isFileDeleted(). Lý do là vai trò chính của metod là xóa một tập tin. Ai đó gọi phương thức này với ý định xóa một tập tin.

Boolean ở đó chỉ để kết hợp lại kết quả của công việc. Mặt khác, hãy xem java.util.Map.isEmpty(). Rõ ràng, bạn gọi một phương thức như vậy để tìm hiểu xem bộ sưu tập có trống không. Bạn không muốn bất cứ thứ gì được thực hiện. Bạn chỉ muốn tìm hiểu những gì hiện trạng.

Vì vậy, cá nhân, tôi chắc chắn sẽ gắn bó với DoStuff().

Đây là một vấn đề rất quan trọng đối với tôi. Nhiều lần khi tôi duy trì mã kế thừa, tôi vấp phải thứ mà tôi gọi là "phương pháp nói dối" cá nhân. Tên gợi ý hành động A, nhưng cơ thể thực hiện hành động B. Ví dụ kỳ lạ nhất là phương pháp getter thay đổi dữ liệu trong cơ sở dữ liệu.

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.