Khi nào và tại sao bạn nên sử dụng void (thay vì ví dụ bool / int)


30

Thỉnh thoảng tôi chạy vào các phương thức mà nhà phát triển đã chọn trả về một cái gì đó không quan trọng đối với chức năng. Ý tôi là, khi nhìn vào mã, nó rõ ràng hoạt động tốt như một voidvà sau một lúc suy nghĩ, tôi hỏi "Tại sao?" Điều này nghe có vẻ quen thuộc phải không?

Đôi khi tôi đồng ý rằng thường thì tốt hơn là trả lại một cái gì đó như một bool, hoặc intsau đó chỉ cần làm một void. Mặc dù vậy, tôi không chắc chắn, trong bức tranh lớn, về những ưu và nhược điểm.

Tùy thuộc vào tình huống, trả về một intcó thể làm cho người gọi nhận biết số lượng hàng hoặc đối tượng bị ảnh hưởng bởi phương thức (ví dụ: 5 bản ghi được lưu vào MSSQL). Nếu một phương thức như "Chèn một cái gì đó" trả về một boolean, tôi có thể có phương thức được thiết kế để trả về truenếu thành công, khác false. Người gọi có thể chọn hành động hoặc không dựa trên thông tin đó.

Mặt khác,

  • Nó có thể dẫn đến một mục đích ít rõ ràng hơn của một cuộc gọi phương thức? Mã hóa xấu thường buộc tôi phải kiểm tra lại nội dung phương thức. Nếu nó trả về một cái gì đó, nó sẽ cho bạn biết rằng phương thức đó thuộc loại bạn phải làm một cái gì đó với kết quả được trả về.
  • Một vấn đề khác là, nếu bạn không biết cách triển khai phương thức, nhà phát triển đã quyết định trả lại cái gì không quan trọng? Tất nhiên bạn có thể nhận xét nó.
  • Giá trị trả về phải được xử lý, khi quá trình xử lý có thể kết thúc tại khung đóng của phương thức.
  • Điều gì xảy ra dưới mui xe? Có phải phương thức được gọi là falsedo lỗi ném? Hoặc nó đã trả về sai do kết quả đánh giá?

Kinh nghiệm của bạn với điều này là gì? Làm thế nào bạn sẽ hành động về điều này?


1
Một hàm trả về void hoàn toàn không phải là một hàm. Nó chỉ là một phương pháp / thủ tục. Trả về voidít nhất cho nhà phát triển biết rằng giá trị trả về của phương thức là không quan trọng; nó thực hiện một hành động, thay vì tính toán một giá trị.
KChaloux

Câu trả lời:


32

Trong trường hợp giá trị trả về bool để chỉ ra sự thành công hay thất bại của một phương thức, tôi thích Trymô hình -prefix được sử dụng trong các phương thức .NET khác nhau.

Ví dụ, một void InsertRow()phương thức có thể đưa ra một ngoại lệ nếu đã tồn tại một hàng có cùng khóa. Câu hỏi là, có hợp lý không khi cho rằng người gọi đảm bảo hàng của họ là duy nhất trước khi gọi insertRow? Nếu câu trả lời là không, thì tôi cũng sẽ cung cấp câu bool TryInsertRow()trả lời sai nếu hàng đã tồn tại. Trong các trường hợp khác, chẳng hạn như lỗi kết nối db, TryInsertRow vẫn có thể đưa ra một ngoại lệ, giả sử rằng việc duy trì kết nối db là trách nhiệm của người gọi.


4
+1 để phân biệt giữa xử lý các thất bại dự kiếnbất ngờ - câu trả lời của riêng tôi không nhấn mạnh điều này đủ.
Péter Török

Hoàn toàn là +1 để cố gắng phát minh ra một nguyên tắc cho các phương thức TryXX, điều này dường như làm cho cả phương pháp thử và phương thức chính dễ duy trì hơn và dễ hiểu mục đích của chúng hơn.
Độc lập

+1 Nói tốt. Chìa khóa cho câu trả lời tốt nhất cho câu hỏi này là đôi khi bạn muốn cung cấp cho người gọi tùy chọn phương pháp quyết đoán sẽ đưa ra một ngoại lệ. Tuy nhiên, trong những trường hợp này, ngoại lệ không nên bị bắt và xử lý giả bằng phương pháp quyết đoán. Vấn đề là người gọi được thực hiện có trách nhiệm. Mặt khác, một mô hình Thử (hoặc Monad) nên nắm bắt và xử lý các trường hợp ngoại lệ, sau đó trả lại một bool hoặc Enum mô tả nếu cần thêm chi tiết. Lưu ý rằng người gọi hy vọng rằng Thử / Monad sẽ KHÔNG BAO GIỜ ném ngoại lệ. Nó giống như một điểm vào.
Suamere

Vì vậy, do một ngoại lệ được dự kiến ​​sẽ không bao giờ xảy ra từ Thử / Monad, nhưng một bool không đủ mô tả để thông báo cho người gọi rằng kết nối db không thành công hoặc yêu cầu http có một trong nhiều giá trị trả về cần thiết để hành động khác nhau, bạn có thể sử dụng Enum thay vì bool cho Thử / Monad của mình.
Suamere

Nếu bạn có TryInsertRow - trả về một bool - nếu, giả sử, có nhiều hơn một ràng buộc duy nhất trong cơ sở dữ liệu - làm thế nào để bạn biết chính xác lỗi đó là gì - và truyền đạt điều đó đến người dùng? Có nên sử dụng loại trả về phong phú hơn có chứa thông báo lỗi / mã & bool thành công không?
niico

28

IMHO trả lại "mã trạng thái" bắt nguồn từ thời điểm lịch sử, trước khi các ngoại lệ trở nên phổ biến trong các ngôn ngữ từ trung cấp đến cao hơn như C #. Ngày nay, tốt hơn hết là ném một ngoại lệ nếu một số lỗi không mong muốn ngăn phương thức của bạn thành công. Bằng cách đó, chắc chắn rằng lỗi không được chú ý và người gọi có thể xử lý khi thích hợp. Vì vậy, trong những trường hợp này, việc phương thức trả về không có gì (nghĩa là void) thay vì cờ trạng thái boolean hoặc mã trạng thái số nguyên là hoàn toàn tốt . (Tất nhiên người ta phải ghi lại những ngoại lệ mà phương thức có thể ném và khi nào.)

Mặt khác, nếu đó là một hàm theo nghĩa chặt chẽ, nghĩa là nó thực hiện một số tính toán dựa trên các tham số đầu vào và dự kiến ​​sẽ trả về kết quả của phép tính đó, kiểu trả về là hiển nhiên.

Có một khu vực màu xám giữa hai người, khi một người có thể quyết định trả lại một số thông tin "phụ" từ phương thức, nếu nó được coi là hữu ích cho người gọi của nó. Giống như ví dụ của bạn về số lượng hàng bị ảnh hưởng trong một chèn. Hoặc đối với một phương thức để đưa một phần tử vào một bộ sưu tập kết hợp, có thể hữu ích để trả về giá trị trước đó được liên kết với khóa đó, nếu có bất kỳ. Việc sử dụng như vậy có thể được xác định bằng cách phân tích cẩn thận các kịch bản sử dụng (đã biết và dự đoán) của API.


4
+1 cho các trường hợp ngoại lệ đối với mã trạng thái. Ngoại lệ (ý định chơi chữ xấu) cho điều này là mẫu TryXX được sử dụng tốt.
Andy Lowry

éter tôi với bạn ở đó. TcF và không im lặng lỗi! Có lẽ, mặc dù, khu vực màu xám. Có thể luôn luôn có sử dụng trả lại một cái gì đó. Các hàng bị ảnh hưởng, ID được chèn mới nhất, PID chạy phần mềm, nhưng tôi vẫn nghĩ dễ dàng hơn khi nghĩ "địa ngục, tôi trả lại cái này" khi phát triển. Sau đó, một năm sau khi mở rộng, "cái gì? Đây là một intint someThing = RunProcess (x) 'điều gì xảy ra nếu nó không thể chạy?"
Độc lập

+1 thông tin bổ sung là lý do tại sao tôi mã hóa các phương thức như thế này bằng các ngôn ngữ cấp cao hơn như C #. Điều này giúp bạn dễ dàng sử dụng thông tin bổ sung khi bạn cần.
tro999

2
-1 Wow, lời nói của bạn rất mâu thuẫn và sai lầm. Bạn không bao giờ nên sử dụng ngoại lệ cho luồng điều khiển hoặc giao tiếp. Chúng vô cùng đắt hơn một điều kiện. Khi nào thì "trước khi các ngoại lệ trở nên phổ biến"? ... ý bạn là trước khi các khối thử / bắt đã trở nên phổ biến? Ngoại lệ đã được phổ biến từ mãi mãi. "Ném một ngoại lệ nếu một số lỗi không mong muốn ..." Làm thế nào để bạn hành động đối với một điều bất ngờ? Tại sao bạn lại bắt một ngoại lệ, sau đó ném lại? Đó là quá đắt. Tại sao bạn nói "lỗi"? Lỗi và ngoại lệ là những thứ khác nhau.
Suamere

1
Và ai sẽ nói một ngoại lệ bị ném sẽ không được chú ý? Không có gì buộc bất cứ ai đăng nhập vào một khối bắt, tại sao không đăng nhập vào một khối "sau đó"? Tại sao "tài liệu những ngoại lệ mà phương pháp có thể ném và khi nào"? Sẽ thế nào nếu chúng ta viết mã tự viết tài liệu và một phương thức chỉ đơn giản trả về một enum với trạng thái kết thúc dự kiến ​​của phương thức đó? Nếu có thể tránh được một ngoại lệ bằng cách kiểm tra trạng thái, chúng sẽ bị bắt và xử lý mà không cần ném.
Suamere

14

Câu trả lời của Peter bao gồm các ngoại lệ tốt. Xem xét khác ở đây liên quan đến phân tách truy vấn lệnh

Nguyên tắc CQS nói rằng một phương thức nên là một lệnh hoặc một truy vấn và không phải cả hai. Các lệnh không bao giờ nên trả về một giá trị, chỉ sửa đổi trạng thái và các truy vấn chỉ nên trả về và không sửa đổi trạng thái. Điều này giữ cho ngữ nghĩa rất rõ ràng và giúp làm cho mã dễ đọc và dễ bảo trì hơn.

Có một vài trường hợp vi phạm nguyên tắc CQS là một ý kiến ​​hay. Đây thường là xung quanh hiệu suất hoặc an toàn chủ đề.


1
-1 Sai và Sai. Thứ nhất, toàn bộ quan điểm của CQS nằm ở Q. Người gọi có thể nhận được các tính toán hoặc cuộc gọi bên ngoài thông qua một số dịch vụ trạng thái mà không sợ thay đổi trạng thái của dịch vụ đó. Ngoài ra, trong khi CQS là một nguyên tắc hữu ích để theo dõi một cách lỏng lẻo, nó chỉ được tuân thủ nghiêm ngặt trong các thiết kế cụ thể. Cuối cùng, ngay cả trong CQS nghiêm ngặt, không có gì nói lệnh không thể trả về giá trị, nó nói rằng nó không nên tính toán hoặc gọi các phép tính bên ngoài và trả về dữ liệu . C hoặc Q có thể cảm thấy thoải mái khi trả về trạng thái kết thúc của chúng, nếu điều đó hữu ích cho người gọi.
Suamere

Ah nhưng số lượng hàng bị ảnh hưởng bởi một Lệnh làm cho việc xây dựng các lệnh mới từ những lệnh cũ dễ dàng hơn nhiều. Nguồn: năm và năm kinh nghiệm.
Joshua

@Joshua: Tương tự như vậy, "thêm bản ghi mới và trả lại ID (đối với một số cấu trúc, số hàng) được tạo trong quá trình thêm" có thể được sử dụng cho các mục đích không thể đạt được một cách hiệu quả.
supercat

Bạn không cần phải trả lại các hàng bị ảnh hưởng. Lệnh nên biết có bao nhiêu sẽ bị ảnh hưởng. Nếu đó là bất cứ thứ gì ngoại trừ # đó, nó sẽ quay trở lại và ném một ngoại lệ vì một điều kỳ lạ vừa xảy ra. Do đó, đó là thông tin mà người gọi không thực sự quan tâm đến nó (trừ khi người dùng cần biết # thực và bạn không thể biết được từ dữ liệu đã cho). Vẫn còn các khu vực màu xám như Id tạo DB, ngăn xếp / hàng đợi trong đó việc lấy dữ liệu thay đổi trạng thái của nó, nhưng tôi thích tạo một "CommandQuery" trong các kịch bản đó để không ai cố gắng làm điều gì đó "kỳ lạ".
Daniel Lorenz

3

Dù con đường là gì bạn sẽ đi bộ với điều này. Không có giá trị trả về trong đó để sử dụng trong tương lai (ví dụ: có các hàm luôn trả về đúng) đây là một sự lãng phí công sức khủng khiếp và không làm cho mã rõ ràng hơn để đọc. Khi bạn có một giá trị trả về, bạn phải luôn sử dụng nó và để có thể sử dụng nó, bạn nên có ít nhất 2 giá trị trả về có thể.


+1 Điều này không trả lời câu hỏi, nhưng nó chắc chắn là thông tin chính xác. Khi đưa ra quyết định, quyết định nên dựa trên nhu cầu. Nếu người gọi không thấy dữ liệu trả về hữu ích, thì không nên thực hiện. Và không ai tìm thấy việc sử dụng để trả lại đúng 100% thời gian cho "bằng chứng trong tương lai". Cứ cho là, nếu "tương lai" giống như ... một vài ngày nữa và có một nhiệm vụ tồn tại cho nó, đó là một câu chuyện khác, lol.
Suamere

1

Tôi đã từng viết rất nhiều hàm void. Nhưng vì tôi đã sử dụng toàn bộ phương pháp bẻ khóa, tôi bắt đầu trả lại cái này thay vì khoảng trống - cũng có thể để ai đó lợi dụng lợi nhuận khiến bạn không thể ngồi xổm với khoảng trống. Và nếu họ không muốn làm bất cứ điều gì với nó, họ có thể bỏ qua nó.


1
Phương pháp xích có thể làm cho thừa kế rườm rà và khó khăn mặc dù. Tôi sẽ không thực hiện phương pháp xâu chuỗi trừ khi bạn có lý do chính đáng để làm điều đó ngoài việc không muốn cho phương thức của mình "khoảng trống".
KallDrexx

Thật. Nhưng thừa kế có thể rườm rà và khó sử dụng.
Wyatt Barnett

Nhìn vào một mã không xác định trả về bất cứ thứ gì (đã đề cập đến toàn bộ đối tượng) rất chú ý chỉ cần kiểm tra luồng bên trong và bên ngoài ..
Độc lập

Tôi không cho rằng bạn đã vào Rubymột lúc nào đó? Đó là những gì đã giới thiệu cho tôi phương pháp xích.
KChaloux

Một điều tôi có với phương pháp xâu chuỗi là nó làm cho nó không rõ ràng hơn cho dù một tuyên bố như return Thing.Change1().Change2();đang mong đợi thay đổi Thingvà trả lại một tham chiếu cho bản gốc, hoặc dự kiến ​​sẽ trả về một tham chiếu đến một điều mới khác với bản gốc, trong khi để lại nguyên bản
supercat
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.