บูลีนกลับมาของ set.add () ถ้ามีเงื่อนไข?


15

ตัวดำเนินการเพิ่มของคลาส set ส่งคืนบูลีนซึ่งเป็นจริงหากองค์ประกอบ (ซึ่งจะถูกเพิ่ม) ยังไม่มีอยู่และเป็นเท็จ กำลังเขียน

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

ถือว่าเป็นสไตล์ที่ดีในแง่ของการเขียนโค้ดที่สะอาด? ฉันสงสัยว่าคุณทำสองสิ่งพร้อมกัน 1) การเพิ่มองค์ประกอบและ 2) การตรวจสอบว่ามีองค์ประกอบอยู่หรือไม่


6
คุณกำลังพูดถึงมาตรฐานjava.util.Setซึ่งจะให้ผลตอบแทนที่เป็นจริงaddเมื่อองค์ประกอบยังไม่มีอยู่ใช่ไหม
user2357112 รองรับโมนิก้า

1
ฉันมักจะพิจารณาการทดสอบที่ตรงข้าม:if (!set.add(entry)) {// entry already present, possibly a case you want to handle}
njzk2

มีอะไรผิดปกติหรือเปล่าที่ทำสองสิ่งพร้อมกัน?
user207421

@ user2357112 ใช่ถูกต้อง
Andreas Braun

คำตอบ:


19

ใช่แล้ว.

จุดปกติของการมีการดำเนินการส่งคืนค่าบูลีนคือคุณสามารถใช้ในการตัดสินใจเช่นภายในifโครงสร้าง วิธีเดียวที่อื่น ๆ ที่คุณอาจจะตระหนักถึงศักยภาพนี้จะเป็นในการจัดเก็บค่าตอบแทนในตัวแปรและจากนั้นทันทีนำมาใช้ใหม่ในซึ่งเป็นเพียงโง่และไม่แน่นอนในทางใดทางหนึ่งที่นิยมในการเขียนif if(operation()) { ... }ดังนั้นไปข้างหน้าและทำอย่างนั้นเราจะไม่ตัดสินคุณ


ขอบคุณสำหรับคำตอบ. @Niels vanReijmersdal ชี้ให้เห็นว่าในคำตอบของเขาการทำเช่นนี้เป็นการแบ่งคำสั่งและการแยกการสืบค้นใช่มั้ย คุณไม่คิดอย่างนั้นหรือ
Andreas Braun

4
@AndreasBraun โดยส่วนตัวฉันคิดว่าการแยกแบบสอบถามคำสั่งเป็นใบ้ มันมักจะเป็นตรรกะสำหรับวิธีการหนึ่งในการดำเนินการและส่งกลับค่าที่เกี่ยวข้องกับการกระทำนั้น การบังคับให้มีการแยกการประดิษฐ์ระหว่างทั้งสองจะนำไปสู่การสร้างรหัสที่ไม่จำเป็น

1
@ dan1111: แน่นอน นอกจากนี้“ การแยกคำสั่งและการสืบค้น” นำไปสู่“ การตรวจสอบแล้วทำ” และรูปแบบการต่อต้านที่คล้ายกันซึ่งสามารถแตกในบริบทแบบมัลติเธรด มันค่อนข้างแปลกที่จะโฆษณาการแยกเช่นนี้เมื่อคนทั่วโลกพยายามที่จะสร้างการทำงานแบบปรมาณูผสมสำหรับโซลูชั่น SMP ที่แข็งแกร่งและมีประสิทธิภาพในเวลาเดียวกัน ...
Holger

2
@ Jörg W Mittag: หน้า 759 ของอะไร เนื่องจากการทำงานแบบปรมาณูนั้นมีภูมิคุ้มกันภายในต่อต้านรูปแบบการต่อต้านแบบ "check then act" จึงไม่มีประโยชน์ที่จะแยกมันออกมาเพียงแค่อ่าน "บททั้งหมด" ของสิ่งที่ต้องค้นหาวิธีทำให้โครงสร้างเธรดปลอดภัย อีกครั้ง เนื่องจากคุณไม่ได้ตั้งชื่อข้อโต้แย้งที่แท้จริงใด ๆ ฉันจึงไม่มี "การคัดค้านต่อข้อโต้แย้งเหล่านั้น" โดยเฉพาะ
Holger

1
@ Jörg W Mittag: เอาละฉันกำลังพูดถึงคำตอบในหน้านี้ท้อใจใช้Set.addเป็นงานเดี่ยวโดยไม่ต้องตั้งชื่ออื่น หากการดำเนินการที่ต้องการคือการเพิ่มองค์ประกอบเดียวและค้นหาว่ามีการเพิ่มหรือไม่มันไม่จำเป็นต้องมีทางเลือกที่มีประสิทธิภาพมากขึ้นที่ยังคงมีความซับซ้อนในการดำเนินการโดยไม่มีผลประโยชน์ ฉันหวังว่าคุณจะรู้ว่าSet.addนี่เป็นวิธีการ JRE ในตัวที่สามารถใช้ได้โดยไม่ต้องศึกษาหนังสือหน้ามากกว่า 700 หน้า
Holger

12

ฉันจะบอกว่ามันไม่สะอาดเท่าที่จะเป็นไปได้เพราะมันบังคับให้ผู้ดูแลรักษารู้หรือค้นหาว่าค่าส่งคืนหมายถึงอะไร หมายความว่าค่าที่มีอยู่แล้วไม่มีอยู่ถูกแทรกสำเร็จหรือไม่ ถ้าคุณไม่ใช้มันมากคุณจะไม่รู้และแม้ว่าคุณจะทำมันก็เป็นภาระทางจิตที่มากกว่า

ฉันต้องการสิ่งต่อไปนี้:

boolean added = set.add(entry);

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

ใช่, verbose มากขึ้นเล็กน้อย, แต่คอมไพเลอร์ควรสร้างรหัสไบต์เดียวกันแน่นอนมาก, และแม้แต่คนที่ไม่ได้ใช้ชุด Java ในปีสามารถติดตามตรรกะโดยไม่ต้องมองอะไรเลย.



9
การค้นหาวิธีการนั้นมีภาระน้อยกว่าการพยายามเข้าใจความตั้งใจของผู้พัฒนาดั้งเดิมสำหรับตัวแปรที่ซ้ำซ้อนซึ่งประกาศอยู่นอกขอบเขตที่ใช้ ใน Java IDEs ที่ทันสมัยทั้งหมดผู้พัฒนาสามารถวางตัวชี้เมาส์ไว้เหนือเมธอดและเข้าถึงเอกสารของมันได้ทันที นักพัฒนาที่ดีต้องทำสิ่งนี้ตลอดเวลาเมื่อทำงานกับ API ที่ไม่คุ้นเคย
Kevin Krumwiede

9
ฉันสอง @Holger หากคุณไม่รู้Set.addคุณไม่มีประสบการณ์และคุณควรมองหาวิธีการเหล่านี้เพื่อเรียนรู้และมีประสบการณ์มากขึ้น
Reinstate Monica

7
notAlreadyPresentไม่ใช่ถ้อยคำที่ดีที่สุด ฉันจะใช้addedและคาดหวังว่าผู้อ่านจะรู้ว่าทำไมค่าจะไม่ถูกเพิ่มลงในชุด
njzk2

3
@JustinTime: การใช้ความคิดเห็นเพื่ออธิบายว่าโค้ดใดบ้างที่ถือว่าเป็นการปฏิบัติที่ไม่ดีอย่างกว้างขวาง รหัสของคุณควรอธิบายด้วยตนเอง ในการตรวจสอบรหัสฉันจะตั้งค่าตัวอย่างของคุณว่ายอมรับไม่ได้
BlueRaja - Danny Pflughoeft

8

หากความจริงหมายถึงความสำเร็จนั่นก็คือโค้ดที่ดีและชัดเจน

มีแบบแผนที่แพร่หลายที่ฟังก์ชันหรือวิธีการส่งกลับค่าเป็นจริง (หรือสิ่งที่ประเมินเป็นจริง) ในความสำเร็จ ตราบใดที่รหัสของคุณเป็นไปตามนั้นฉันคิดว่าการวางวิธีการในเงื่อนไขนั้นใช้ได้

รหัสเช่นนี้มีความยุ่งเหยิงโดยไม่จำเป็นในมุมมองของฉัน:

boolean frobulate_succeeded = thing.frobulate();

if (frobulate_succeeded) {
    ...
}

รู้สึกเหมือนคุณกำลังทำซ้ำตัวเอง

อย่างไรก็ตามคำถามนั้นคลุมเครือในความหมายของค่าส่งคืน คุณพูดว่า "บูลีนซึ่งระบุว่าองค์ประกอบที่เพิ่มเข้ามามีอยู่แล้ว" ซึ่งอาจหมายความว่าจริงหมายถึงองค์ประกอบที่มีอยู่ (และเพิ่มไม่ได้เกิดขึ้น) หากเป็นกรณีนี้ฉันจะเปลี่ยนพฤติกรรมการส่งคืนของวิธีเป็นการทั่วไปมากขึ้น หากไม่สามารถทำได้ฉันจะเพิ่มตัวแปรกลางพิเศษที่อนุญาตให้คุณติดป้ายกำกับผลลัพธ์การส่งคืนในโค้ดของคุณ (ตามที่ผู้อื่นแนะนำ)


ความสำเร็จหมายถึงอะไรในบริบทของการเพิ่มรายการในชุด การไม่โยนข้อยกเว้นหมายถึงความสำเร็จ จริงและเท็จหมายถึงบางสิ่งที่เฉพาะเจาะจงมากขึ้นในบริบทนี้
Reinstate Monica

@ Solomonoff'sSecret ในกรณีนี้ข้อยกเว้นหมายถึงชุดที่ปฏิเสธที่จะเพิ่มองค์ประกอบfalseหมายความว่าองค์ประกอบที่ไม่ได้เพิ่มเพราะมันมีอยู่แล้วและtrueหมายความว่าองค์ประกอบที่ไม่ได้มีอยู่แล้ว ในฐานะที่เป็นadd()เพิ่มองค์ประกอบให้กับชุดถ้าชุดไม่ได้มีอยู่แล้วtrueดังนั้นหมายความว่าองค์ประกอบถูกเพิ่มเข้าไปในชุดเรียบร้อยแล้ว
Justin Time - Reinstate Monica

@ จัสตินไทม์คุณกำลังเพิ่มการตัดสินคุณค่าของคุณเองในสองกรณี การตีความอีกอย่างหนึ่งก็คือความสำเร็จคือรายการนั้นอยู่ในชุดเมื่อวิธีการส่งคืน หากรายการนั้นอยู่ในชุดเรียบร้อยแล้วaddสำเร็จโดยไม่ต้องทำอะไรเลย หากรายการไม่ได้อยู่ในชุดแล้วaddประสบความสำเร็จโดยการเพิ่มรายการในชุด การตีความใดที่ถูกต้องเป็นไปโดยพลการ คำจำกัดความของความสำเร็จโดยพลการน้อยกว่านั้นมาจากภาษา: วิธีการประสบความสำเร็จถ้ามันกลับมาตามปกติ
Reinstate Monica

1
คำจำกัดความของความสำเร็จโดยพลการอย่างน้อยที่สุดก็คือวิธีการดำเนินงานที่ประสบความสำเร็จ ถ้าฉันมีวิธีการfirstLessThanSecond(int l, int r)และมันจะคืนกลับมาtrueถ้าl > rหรือfalseถ้าl <= rอย่างนั้นวิธีนั้นก็ไม่สำเร็จแม้ว่ามันจะกลับมาตามปกติ
Justin Time - Reinstate Monica

1
@ จัสตินไทม์addเป็นตัวย่อของสัญญาอย่างรุนแรง เอกสารเริ่มต้นด้วย "เพิ่มองค์ประกอบที่ระบุในชุดนี้หากยังไม่ปรากฏ" ซึ่งเป็นที่น่าพอใจว่ารายการนั้นอยู่ในชุดแล้วหรือไม่ คุณมีอิสระในการตีความค่าตอบแทน แต่คุณต้องการ แต่ในที่สุดมันก็เป็นเพียงการตีความของคุณ และในตัวอย่างที่สองของคุณวิธีการประเมินว่าเงื่อนไขเป็นจริง หากเงื่อนไขเป็นเท็จวิธีการจะไม่ล้มเหลวอย่างแน่นอนเนื่องจากได้ประเมินเงื่อนไขเสร็จเรียบร้อยแล้ว
Reinstate Monica

2

ฉันว่ามันเหมือน C มาก เวลาส่วนใหญ่ฉันต้องการให้ตัวแปรที่มีชื่อแบบอธิบายสำหรับผลการกลายพันธุ์และไม่มีการกลายพันธุ์เกิดขึ้นในifสภาพ

คอมไพเลอร์จะกำจัดตัวแปรนี้ถ้ามันถูกนำมาใช้ใหม่ทันที มนุษย์จะมีเวลาอ่านที่มาได้ง่ายขึ้น สำหรับฉันมันสำคัญกว่า

หากใครบางคนต้องขยายเงื่อนไขโดยการเพิ่มand/ orข้อไปที่เงื่อนไขถ้าพวกเขาอาจท้ายไม่ได้โทร.add()ในบางกรณีเนื่องจากการประเมินผลการลัดวงจร เว้นแต่จะมีการลัดวงจรเป็นพิเศษคาดว่านี้อาจจบลงเป็นข้อผิดพลาด


ถ้าฉันเขียนif (set.contains(entry)){set.add(entry); //do more stuff}มันก็ถูกกำจัดโดยคอมไพเลอร์ด้วยเหรอ?
Andreas Braun

1
@ AndreasBraun: ฉันไม่คิดอย่างนั้น คอมไพเลอร์มีความคิดเกี่ยวกับการเชื่อมโยงความหมายระหว่างไม่มีและcontains addนอกจากนี้ยังทำงานสองครั้งของการค้นหาentryในชุด; สิ่งนี้อาจมีบทบาทสำหรับฉากที่มีขนาดใหญ่มากและลูปที่เบามาก
9000

1
ดังนั้นฉันคิดว่าคุณไม่อยากเขียนif (set.contains(entry)){set.add(entry); //do more stuff}แต่ไปกับคำตอบของคาร์ลบีเลเฟลด์แทน
Andreas Braun

@AndreasBraun: ถูกต้อง (ตอบคำถามนั้นได้ดีขึ้น)
9000

1
เป็นจุดที่ดี การกลายพันธุ์ในไอเอฟเอสนั้นเป็นเรื่องยุ่งยากเนื่องจากผู้พัฒนาในอนาคตอาจมีเงื่อนไขอื่น ๆ ที่มีวงจรไฟฟ้าสั้น
njzk2

0

รหัสของคุณดูเหมือนว่าจะทำลายแยกคำสั่ง Query ซึ่งจะกล่าวถึงในหนังสือ Clean Codeและวิดีโอโครงสร้างฟังก์ชั่น ดังนั้นจากมุมมองของรหัสที่สะอาดฉันคิดว่ามันไม่ถือว่าเป็นสไตล์ที่ดี

“ รหัสที่สะอาดง่ายและตรงไปตรงมา ทำความสะอาดรหัสอ่านเช่นร้อยแก้วที่เขียนดี โค้ดที่สะอาดไม่เคยปิดบังเจตนาของนักออกแบบ แต่เต็มไปด้วยนามธรรมที่คมชัดและเส้นควบคุมที่ตรงไปตรงมา

- ผู้เขียน Grady Booch ของการวิเคราะห์เชิงวัตถุและการออกแบบด้วยแอปพลิเคชัน”

สำหรับฉันความตั้งใจของรหัสของคุณไม่ชัดเจน ถ้าทั้งสองดำเนินการเมื่อรายการถูกเพิ่มสำเร็จหรือยังเมื่อมันมีอยู่แล้ว? อะไรadd()กลับมา? รายการ? รหัสข้อผิดพลาด?


2
ใช่นั่นคือสิ่งที่ฉันคิดเช่นกัน แต่ฉันก็คิดว่า @Kilian Foth มีประเด็น ถ้าฉันต้องการแยกแบบสอบถามและคำสั่งฉันจะต้องเขียนแบบif (set.contains(entry)){set.add(entry); //do more stuff}ที่ดูเหมือนว่าโง่ คุณทำอะไรกับเรื่องนี้?
Andreas Braun

ฉันไม่ทราบโดเมนและบริบทของรหัสนี้เพื่อแนะนำชื่อที่ดี แต่ฉันจะใช้ฟังก์ชันนี้โดยตั้งใจ ตัวอย่างเช่น: doesEntryExistOrCreateEntry (รายการ) สิ่งนี้อาจมีตรรกะ () + add () ของคุณและส่งคืนบูลีน คำถามที่คล้ายกันที่นี่: softwareengineering.stackexchange.com/questions/149708/ …ซึ่งกล่าวถึงการปฏิบัตินี้นอกรหัสสะอาด
Niels van Reijmersdal

6
ฉันคิดว่ากฎการแยกแบบสอบถามคำสั่งนั้นเป็นใบ้โดยเฉพาะเมื่อใช้กับกรณีเช่นนี้ซึ่งวิธีการทั้งสองดำเนินการกระทำและส่งกลับสถานะความสำเร็จของการกระทำ แต่คุณไม่ต้องอธิบายว่ากฎนั้นคืออะไรหรือให้เหตุผลมากพอสมควร ลงคะแนนด้วยเหตุผลเหล่านี้

1
"เพิ่ม () ส่งคืนอะไร" ฉันคาดหวังว่านักพัฒนาจาวาใด ๆ ที่ทำโค้ดตรวจสอบเพื่อทราบCollectionAPI
njzk2

3
ตกลงกับ @ dan1111 มันเป็นโดยเฉพาะอย่างยิ่งเป็นใบ้เพราะมันคือการจัดเรียงของสิ่งที่ทำให้ดินอุดมสมบูรณ์สำหรับเงื่อนไขการแข่งขัน
Paul Draper
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.