Boolean return của set.add () trong nếu có điều kiện?


15

Toán tử add của lớp set trả về một boolean là true nếu phần tử (được thêm vào) chưa có ở đó và ngược lại là false. Đang viết

if (set.add(entry)) {
    //do some more stuff
}

coi phong cách tốt về mặt viết mã sạch? Tôi tự hỏi vì bạn làm hai việc cùng một lúc. 1) thêm phần tử và 2) kiểm tra xem phần tử có tồn tại không.


6
Bạn đang nói về tiêu chuẩn java.util.Set, sẽ trả về đúng addkhi phần tử chưa có, phải không?
user2357112 hỗ trợ Monica

1
Tôi thường sẽ xem xét các bài kiểm tra ngược lại:if (!set.add(entry)) {// entry already present, possibly a case you want to handle}
njzk2

Có điều gì đó sai khi làm hai việc cùng một lúc?
dùng207421

@ user2357112 đúng, đúng vậy.
Andreas Braun

Câu trả lời:


19

Vâng, đúng vậy.

Điểm thông thường của việc có một hoạt động trả về giá trị Boolean là bạn có thể sử dụng nó để đưa ra quyết định, tức là trong một ifcấu trúc. Cách khác duy nhất bạn có thể nhận ra tiềm năng này là lưu trữ giá trị trả về trong một biến và sau đó sử dụng lại ngay lập tức if, điều này thật ngớ ngẩn và chắc chắn không phải là cách viết thích hợp hơn if(operation()) { ... }. Vì vậy, cứ tiếp tục và làm điều đó, chúng tôi sẽ không phán xét bạn (vì điều đó).


Cảm ơn câu trả lời của bạn. Như @Niels vanReijmersdal chỉ ra trong câu trả lời của anh ta, việc này phá vỡ lệnh và truy vấn, phải không? Bạn không nghĩ rằng đó là vấn đề?
Andreas Braun

4
@AndreasBraun cá nhân Tôi nghĩ rằng phân tách truy vấn lệnh là ngu ngốc. Nó thường hợp lý khi một phương thức vừa thực hiện một hành động vừa trả về một giá trị liên quan đến hành động đó. Thực thi một sự tách biệt giả tạo giữa hai dẫn đến mã dài dòng không cần thiết.

1
@ dan1111: thật vậy. Hơn nữa, tách biệt lệnh và truy vấn của mối quan hệ, dẫn đến kiểm tra trên mạng, sau đó hành động và các kiểu chống tương tự, có thể phá vỡ các bối cảnh đa luồng. Thật kỳ lạ khi quảng cáo một sự tách biệt như vậy, khi phần còn lại của thế giới đang cố gắng xây dựng các hoạt động hợp nhất nguyên tử cho các giải pháp SMP mạnh mẽ và hiệu quả cùng lúc với nhau
Holger

2
@ Jorg W Mittag: trang 759 của cái gì? Vì một hoạt động nguyên tử về bản chất là miễn dịch với kiểm tra trên mạng, sau đó hành động chống mẫu, nên việc tách nó ra chỉ để đọc toàn bộ một chương của bất cứ điều gì để tìm hiểu, làm thế nào để tạo ra sợi chỉ an toàn lần nữa. Vì bạn không nêu tên bất kỳ đối số thực tế nào, tôi không có phản đối cụ thể nào đối với những đối số đó.
Holger

1
@ Jorg W Mittag: Chà, tôi đang nói về câu trả lời trên trang này, không khuyến khích sử dụng Set.addnhư một thao tác đơn lẻ mà không đặt tên thay thế. Nếu hoạt động dự định là thêm một yếu tố duy nhất và tìm hiểu xem nó đã được thêm vào chưa, thì không cần phải có một sự thay thế mạnh mẽ hơn mà vẫn làm phức tạp hoạt động mà không có lợi ích. Tôi hy vọng bạn biết rằng đó Set.addlà một phương pháp JRE dựng sẵn có thể được sử dụng mà không cần nghiên cứu một cuốn sách> 700 trang trước.
Holger

12

Tôi sẽ nói rằng nó không sạch nhất có thể, bởi vì nó buộc người bảo trì phải biết hoặc tìm kiếm những gì giá trị trả về biểu thị. Có nghĩa là giá trị đã tồn tại, chưa tồn tại, đã được chèn thành công? Nếu bạn không sử dụng nó nhiều, bạn sẽ không biết, và thậm chí nếu bạn làm thế, thì điều đó còn nặng nề hơn nhiều.

Tôi thích những điều sau đây:

boolean added = set.add(entry);

if (added) {
    //do some more stuff
}

Vâng, dài dòng hơn một chút, nhưng trình biên dịch sẽ tạo ra khá nhiều mã byte chính xác và thậm chí những người không sử dụng bộ Java trong nhiều năm có thể theo logic mà không cần tìm kiếm gì.



9
Tìm kiếm một phương thức ít gánh nặng hơn là cố gắng hiểu ý định của nhà phát triển ban đầu về một biến dự phòng được khai báo bên ngoài phạm vi mà nó được sử dụng. Trong tất cả các IDE Java hiện đại, nhà phát triển có thể di con trỏ chuột lên một phương thức và truy cập ngay vào tài liệu của nó. Các nhà phát triển giỏi làm điều này liên tục khi làm việc với bất kỳ API lạ nào.
Kevin Krumwiede

9
Tôi thứ hai @Holger. Nếu bạn không biết Set.add, bạn thiếu kinh nghiệm và bạn nên tìm kiếm các phương pháp này để tìm hiểu chúng và trở nên có kinh nghiệm hơn.
Phục hồi

7
notAlreadyPresentkhông phải là từ ngữ tốt nhất. Tôi sẽ sử dụng addedvà mong người đọc biết tại sao giá trị sẽ không được thêm vào Tập hợp.
njzk2

3
@JustinTime: Sử dụng các bình luận để mô tả những gì mã được coi là thực hành xấu. Mã của bạn nên được tự mô tả. Trong bài đánh giá mã, tôi sẽ đánh dấu ví dụ của bạn là không thể chấp nhận được.
BlueRaja - Daniel Pflughoeft

8

Nếu đúng có nghĩa là thành công, thì đó là mã tốt, rõ ràng.

Có một quy ước phổ biến rằng một hàm hoặc phương thức trả về giá trị true (hoặc một cái gì đó đánh giá là đúng) về thành công. Miễn là mã của bạn tuân theo điều đó, tôi nghĩ việc đưa phương thức vào điều kiện là ổn.

Mã như thế này là lộn xộn không cần thiết trong quan điểm của tôi:

boolean frobulate_succeeded = thing.frobulate();

if (frobulate_succeeded) {
    ...
}

Cảm giác như bạn đang lặp lại chính mình.

Tuy nhiên, câu hỏi mơ hồ về ý nghĩa của giá trị trả về. Bạn nói "một boolean cho biết phần tử đã thêm đã tồn tại chưa", điều này có thể ngụ ý rằng true có nghĩa là phần tử tồn tại (và phần bổ sung không xảy ra). Nếu đó là trường hợp, tôi lý tưởng sẽ thay đổi hành vi trả về của phương thức thành thông thường hơn. Nếu điều đó là không thể, tôi sẽ thêm một biến trung gian bổ sung cho phép bạn dán nhãn rõ ràng kết quả trả về trong mã của bạn (như được đề xuất bởi người khác).


Thành công có ý nghĩa gì trong bối cảnh thêm một mục vào một tập hợp? Không ném một ngoại lệ có nghĩa là thành công; đúng so với sai có nghĩa là một cái gì đó cụ thể hơn trong bối cảnh này.
Phục hồi

@ Solomonoff'sSecret Trong trường hợp này, một ngoại lệ có nghĩa là tập hợp đã từ chối thêm phần tử, falsecó nghĩa là phần tử không được thêm vào vì nó đã được chứa và truecó nghĩa là phần tử đã không được chứa. Khi add()thêm phần tử vào tập hợp nếu tập hợp chưa chứa nó, truedo đó có nghĩa là phần tử đã được thêm thành công vào tập hợp.
Thời gian của Justin - Tái lập lại

@JustinTime Bạn đang thêm các phán đoán giá trị của riêng bạn vào hai trường hợp. Một cách giải thích khác là thành công là mục nằm trong tập hợp khi phương thức trả về. Nếu vật phẩm đã có trong bộ, addthành công mà không phải làm gì cả. Nếu mục chưa có trong tập hợp, addthành công bằng cách thêm mục vào tập hợp. Mà giải thích là chính xác. Định nghĩa thành công ít độc đoán là từ ngôn ngữ: phương thức thành công nếu nó trả về bình thường.
Phục hồi lại

1
Định nghĩa thành công ít độc đoán nhất là phương thức thực hiện thành công nhiệm vụ mà nó đặt ra để thực hiện. Nếu tôi có một phương thức firstLessThanSecond(int l, int r)và nó trả về truenếu l > rhoặc falsenếu l <= r, thì phương thức đó không thành công, mặc dù nó trả về bình thường.
Thời gian của Justin - Tái lập lại

1
@JustinTime addlà tên viết tắt nghiêm trọng của hợp đồng. Tài liệu bắt đầu bằng "Thêm phần tử được chỉ định vào bộ này nếu nó chưa có sẵn", điều này được thỏa mãn cho dù mục đó đã có trong bộ hay chưa. Bạn có thể tự do giải thích giá trị trả về theo cách bạn muốn nhưng cuối cùng nó chỉ là cách giải thích của bạn. Và trong ví dụ thứ hai của bạn, phương thức đánh giá xem một điều kiện có đúng không. Nếu điều kiện sai, phương thức chắc chắn không thất bại, vì nó đã đánh giá thành công điều kiện.
Phục hồi

2

Tôi muốn nói nó rất giống C. Hầu hết thời gian tôi muốn có một biến được đặt tên mô tả cho kết quả đột biến và không có đột biến xảy ra trong một ifđiều kiện.

Một trình biên dịch sẽ loại bỏ biến này nếu nó được sử dụng lại ngay lập tức. Một con người sẽ có thời gian dễ dàng hơn để đọc nguồn; Đối với tôi, nó quan trọng hơn.

Nếu ai đó phải mở rộng điều kiện bằng cách thêm một mệnh đề and/ orvào điều kiện if, cuối cùng họ có thể không gọi .add()trong một số trường hợp do đánh giá ngắn mạch. Trừ khi ngắn mạch được dự đoán cụ thể, điều này có thể kết thúc như một lỗi.


Nếu tôi viết if (set.contains(entry)){set.add(entry); //do more stuff}thì cũng bị loại bởi trình biên dịch?
Andreas Braun

1
@AndreasBraun: Tôi không nghĩ vậy; trình biên dịch không có ý tưởng về liên kết ngữ nghĩa giữa containsadd. Ngoài ra, nó làm việc gấp đôi tìm kiếm entrytrong bộ; điều này có thể đóng một vai trò cho các bộ rất lớn và các vòng rất nhẹ.
9000

1
Vì vậy, tôi cho rằng bạn thà không viết if (set.contains(entry)){set.add(entry); //do more stuff}mà thay vào đó đi với câu trả lời của Karl Bielefeldt?
Andreas Braun

@AndreasBraun: chính xác (nêu lên câu trả lời).
9000

1
một điểm tốt. Đột biến trong ifs là khó khăn, vì một nhà phát triển trong tương lai có thể có các điều kiện khác với ngắn mạch.
njzk2

0

Mã của bạn dường như phá vỡ phân tách truy vấn lệnh . Điều này được thảo luận trong sách Clean Code và video Cấu trúc chức năng . Vì vậy, từ góc độ Clean Code tôi nghĩ rằng nó không được coi là phong cách tốt.

Mã sạch Clean là đơn giản và trực tiếp. Mã sạch đọc như văn xuôi được viết tốt. Mã sạch không bao giờ che khuất ý định của nhà thiết kế mà thay vào đó là đầy đủ các khái niệm trừu tượng và các dòng điều khiển đơn giản.

- Grady Booch tác giả của Phân tích và Thiết kế hướng đối tượng với các ứng dụng

Đối với tôi mục đích của mã của bạn là không rõ ràng. Là nếu cả hai thực hiện khi mục nhập được thêm thành công, hoặc cả khi nó đã tồn tại? Điều gì add()trở lại? các mặt hàng? Mã lỗi?


2
Vâng, đó là những gì tôi nghĩ là tốt. Nhưng tôi cũng nghĩ @Kilian Foth có một điểm. Nếu tôi muốn tách biệt truy vấn và ra lệnh, tôi sẽ phải viết if (set.contains(entry)){set.add(entry); //do more stuff}những thứ có vẻ ngớ ngẩn. Bạn nghĩ gì về điều này?
Andreas Braun

Tôi không biết tên miền và ngữ cảnh của mã này để đề xuất một tên hay, nhưng tôi sẽ bọc nó trong một chức năng với mục đích rõ ràng. Ví dụ: doesEntryExistOrCreateEntry (entry), cái này có thể chứa logic chứa () + add () của bạn và trả về một boolean. Câu hỏi tương tự ở đây: softwareengineering.stackexchange.com/questions/149708/ , trong đó thảo luận về thực hành này bên ngoài mã sạch.
Niels van Reijmersdal

6
Tôi nghĩ rằng quy tắc phân tách truy vấn lệnh là ngu ngốc, đặc biệt khi áp dụng cho một trường hợp như thế này, trong đó một phương thức vừa thực hiện một hành động vừa trả về trạng thái thành công của hành động. Nhưng ngoài ra, bạn không giải thích quy tắc này là gì hoặc cung cấp nhiều theo cách biện minh cho quy tắc đó. Downvote vì những lý do này.

1
"Điều gì thêm () trở lại?" Tôi hy vọng bất kỳ nhà phát triển java nào thực hiện đánh giá mã sẽ biết Collectionapi.
njzk2

3
Đồng ý với @ dan1111. Điều đó đặc biệt ngớ ngẩn vì đó là thứ tạo nên mảnh đất màu mỡ cho điều kiện chủng tộc.
Paul Draper
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.