มีเหตุผลที่ดีที่จะใช้ส่วนต่อประสาน Collection ของ Java หรือไม่


11

ฉันได้ยินข้อโต้แย้งว่าคุณควรใช้อินเทอร์เฟซทั่วไปที่มีอยู่เพื่อที่คุณจะไม่ได้ผูกติดอยู่กับการใช้อินเทอร์เฟซนั้น ตรรกะนี้ใช้กับอินเตอร์เฟสเช่นjava.util.Collectionหรือไม่?

ฉันอยากเห็นสิ่งต่อไปนี้:

List<Foo> getFoos()

หรือ

Set<Foo> getFoos()

แทน

Collection<Foo> getFoos()

ในกรณีสุดท้ายฉันไม่รู้ว่าชุดข้อมูลแบบไหนที่ฉันจัดการในขณะที่ในสองกรณีแรกฉันสามารถตั้งสมมติฐานบางอย่างเกี่ยวกับการสั่งซื้อและเอกลักษณ์ ไม่java.util.Collectionมีนอกประโยชน์ของการเป็นผู้ปกครองตรรกะสำหรับทั้งสองชุดและรายการ?

หากคุณพบรหัสที่ใช้การรวบรวมเมื่อทำการตรวจสอบรหัสคุณจะทราบได้อย่างไรว่าการใช้งานนั้นถูกต้องหรือไม่


3
คุณเคยสังเกตเห็นใน java.security.cert หรือไม่ว่าประเภทคืนหนึ่งคือCollection<List<?>>อะไร พูดคุยเกี่ยวกับการเข้ารหัสสยองขวัญ!
Macneil

1
@ Macneil ฉันไม่ทราบว่าคุณหมายถึงคลาสใด โดยพื้นฐานแล้วจะบอกคุณว่าคุณมีการสะสม (เช่นมีสิ่งต่าง ๆ ที่ไม่มีลำดับที่สมเหตุสมผล) ของรายการ (เช่นมีสิ่งที่มีการสั่งซื้อที่สมเหตุสมผล) ของวัตถุ (เช่นรายการที่ประเภทที่เราไม่รู้จักแบบคงที่สำหรับ ไม่ว่าด้วยเหตุผลใด). ดูเหมือนจะไม่สมเหตุสมผลสำหรับฉัน
Zero3

คำตอบ:


13

บทคัดย่อมีชีวิตยืนยาวกว่าการนำไปใช้งาน

โดยทั่วไปยิ่งการออกแบบของคุณเป็นนามธรรมนานเท่าใดก็ยิ่งมีแนวโน้มที่จะมีประโยชน์มากขึ้นเท่านั้น ดังนั้นเนื่องจากคอลเลกชันเป็นนามธรรมมากขึ้นว่าเป็นอินเทอร์เฟซย่อยดังนั้นการออกแบบ API ที่ยึดตามคอลเลกชันจึงมีแนวโน้มที่จะยังคงมีประโยชน์มากกว่าหนึ่งรายการตามรายการ

แต่หลักการที่ครอบคลุมคือการใช้สิ่งที่เป็นนามธรรมที่เหมาะสมที่สุด ดังนั้นหากคอลเลกชันของคุณจะต้องสนับสนุนองค์ประกอบที่สั่งซื้อจากนั้นสั่งรายการถ้าไม่มีรายการที่ซ้ำกันให้สั่งชุดและอื่น ๆ

หมายเหตุเกี่ยวกับการออกแบบอินเตอร์เฟสทั่วไป

เนื่องจากคุณสนใจที่จะใช้อินเทอร์เฟซการเก็บรวบรวมกับข้อมูลทั่วไปคุณอาจเป็นประโยชน์ต่อไปนี้ Java ที่มีประสิทธิภาพโดย Joshua Blochแนะนำวิธีการต่อไปนี้เมื่อออกแบบอินเตอร์เฟสที่ต้องพึ่งพาข้อมูลทั่วไป: Producers Extend, Consumerers Super

นอกจากนี้ยังเป็นที่รู้จักกันเป็นกฎเต้า เป็นหลักถ้าคอลเลกชันทั่วไปที่ผลิตข้อมูลถูกส่งไปยังคลาสของคุณลายเซ็นควรมีลักษณะเช่นนี้:

public void pushAll(Collection<? extends E> producerCollection) {}

ดังนั้นประเภทอินพุตสามารถเป็น E หรือคลาสย่อยใด ๆ ของ E (E หมายถึงทั้งคลาสย่อยและคลาสของตัวเองในภาษาจาวา)

ในทางกลับกันคอลเลกชันทั่วไปที่ส่งผ่านเพื่อใช้ข้อมูลควรมีลายเซ็นดังนี้:

public void popAll(Collection<? super E> consumerCollection) {}

วิธีการที่ถูกต้องจะจัดการกับ superclass อีโดยรวมใด ๆ โดยใช้วิธีการนี้จะทำให้อินเตอร์เฟซของคุณน้อยที่น่าแปลกใจให้กับผู้ใช้ของคุณเพราะคุณจะสามารถที่จะผ่านในCollection<Number>และCollection<Integer>และมีพวกเขาได้รับการรักษาอย่างถูกต้อง


6

Collectionอินเตอร์เฟซและรูปแบบที่อนุญาตส่วนใหญ่Collection<?>เป็นที่ดีสำหรับพารามิเตอร์ที่คุณยอมรับ จากการใช้งานในไลบรารี Java นั้นพบได้ทั่วไปว่าเป็นชนิดพารามิเตอร์มากกว่าชนิดส่งคืน

สำหรับประเภทการคืนสินค้าฉันคิดว่าประเด็นของคุณถูกต้อง: หากผู้คนคาดว่าจะสามารถเข้าถึงได้พวกเขาควรรู้ถึงลำดับ (ในความหมาย Big-O) ของการดำเนินการ ฉันจะทำซ้ำสิ่งที่Collectionส่งคืนและเพิ่มลงในคอลเล็กชันอื่น แต่มันก็ดูค่อนข้างบ้าที่จะโทรหาcontainsโดยไม่รู้ว่ามันเป็นการดำเนินงาน O (1), O (log n) หรือ O (n) แน่นอนเพียงเพราะคุณSetไม่ได้หมายความว่ามันเป็นแฮชเซ็ตหรือชุดที่เรียงลำดับ แต่ในบางจุดคุณจะทำการสันนิษฐานว่าอินเทอร์เฟซได้รับการใช้งานอย่างสมเหตุสมผล (จากนั้นคุณจะต้องวางแผน B หากสมมติฐานของคุณ แสดงว่าไม่ถูกต้อง)

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


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

นอกจากนี้หากสิ่งที่คุณรู้คือสิ่งที่เป็นคอลเลกชันคุณไม่มีความคิดว่ามันสามารถมีรายการที่ซ้ำกันหรือไม่ ในกรณีที่คอลเลกชันที่ส่งคืนอาจเหมาะสมคือถ้าคุณมีคอลเลกชันที่ไม่มีรายการที่ซ้ำกันและไม่มีคำสั่งที่สำคัญ (ซึ่งโดยปกติจะเป็นชุด) แต่สำหรับเหตุผลที่ดีบางอย่างการใช้วิธีการส่งคืน ใช้รายการ คุณไม่ต้องการส่งคืน List เพราะมันหมายถึงลำดับสำคัญ แต่คุณไม่สามารถส่งคืน Set ได้โดยไม่ต้องผ่าน rigmarole ในการสร้าง ดังนั้นคุณกลับคอลเลกชัน
Tom Anderson

@Tom: จุดดี!
Macneil

5

ฉันจะดูจากมุมมองที่ตรงกันข้ามและถาม:

หากคุณพบรหัสที่ใช้รายการ <> เมื่อทำการตรวจสอบรหัสคุณจะทราบได้อย่างไรว่าการใช้งานนั้นถูกต้องหรือไม่

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

มีหลายกรณีที่คุณจะใช้คอลเลกชันเพื่อจุดประสงค์ในการอ่านอย่างเดียวเติมข้อมูลทั้งหมดในครั้งเดียววนซ้ำทั้งหมด - คุณจำเป็นต้องทำดัชนีสิ่งด้วยตนเองหรือไม่?

เพื่อให้เป็นตัวอย่างที่แท้จริง บอกว่าฉันทำแบบสอบถามง่ายๆในฐานข้อมูล ( SELECT id,name,rep FROM people WHERE name LIKE '%squared') ฉันดึงข้อมูลที่เกี่ยวข้องเติมวัตถุบุคคลและใส่ em ลงใน PersonList)

  • ฉันจำเป็นต้องเข้าถึงด้วยดัชนีหรือไม่ - จะไร้ความหมาย ไม่มีการแมประหว่างดัชนีและ ID
  • ฉันต้องแทรกที่ดัชนีหรือไม่ - ไม่ DBMS จะตัดสินใจว่าจะใส่ไว้ที่ไหนถ้าฉันเพิ่มเลย

ดังนั้นฉันจะให้เหตุผลอะไรสำหรับวิธีพิเศษเหล่านั้น? (ซึ่งจะไม่มีการใช้งานใน PersonList ของฉันอยู่ดี)


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

หากคุณได้ทำการสอบถาม DB การเปรียบเทียบวัตถุที่ได้ผลลัพธ์ 2 รายการที่มีค่าเท่ากับ () ไม่ควรเกิดขึ้นจริงเลยดังนั้นคุณต้องหาวิธีอื่นในการเปรียบเทียบวัตถุสำหรับการทำซ้ำ (เช่นมีชื่อเดียวกันรหัสเดียวกันหรือไม่ ทั้งสอง?) คุณต้องบอก DAO ของคุณว่าจะเปรียบเทียบอย่างไรถ้าเป็นการลบรายการที่ซ้ำกันออก - แต่เนื่องจากคุณเป็นผู้ใช้ที่ตัดสินใจว่าจะมีรายการที่ซ้ำกันอยู่หรือไม่ - ง่ายกว่าที่จะทำกับชุดสะสมจากรหัสโทร (เพื่อหลีกเลี่ยงเลเยอร์สิ่งที่เป็นนามธรรมมากขึ้นเพื่อแจ้ง DAO ถึงวิธีการตรวจสอบความเท่าเทียมกันที่เป็นไปได้ทั้งหมดบนโลก)
Mark H

ตกลง แต่เรากำลังใช้ Hibernate และตรวจสอบให้แน่ใจว่าเอนทิตีของเราใช้เท่ากับ () ดังนั้นเมื่อ DAO ส่งคืนเอนทิตีเราสามารถทำ HashSet () ใหม่ได้อย่างรวดเร็ว addAll (ผลลัพธ์) และส่งคืนกลับไปยังเมธอดที่เรียก
Fil
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.