นี่คือปัญหาที่ฉันพบบ่อย: ให้มีโครงการร้านค้าบนเว็บที่มีระดับสินค้า ฉันต้องการเพิ่มคุณสมบัติที่อนุญาตให้ผู้ใช้โพสต์คำวิจารณ์ไปยังผลิตภัณฑ์ ดังนั้นฉันมีคลาสรีวิวซึ่งอ้างอิงผลิตภัณฑ์ ตอนนี้ฉันต้องการวิธีการที่แสดงความเห็นทั้งหมดในผลิตภัณฑ์ มีความเป็นไปได้สองอย่าง:
(A)
public class Product {
...
public Collection<Review> getReviews() {...}
}
(B)
public class Review {
...
static public Collection<Review> forProduct( Product product ) {...}
}
จากการดูรหัสฉันเลือก (A): มันไม่คงที่และไม่ต้องการพารามิเตอร์ อย่างไรก็ตามฉันรู้สึกว่า (A) ละเมิดหลักการความรับผิดชอบเดี่ยว (SRP) และหลักการเปิด (OCP) ในขณะที่ (B) ไม่:
(SRP) เมื่อฉันต้องการเปลี่ยนวิธีการรวบรวมความเห็นสำหรับผลิตภัณฑ์ฉันต้องเปลี่ยนระดับสินค้า แต่ควรมีเหตุผลเพียงประการเดียวว่าทำไมต้องเปลี่ยนระดับผลิตภัณฑ์ และนั่นไม่ใช่ความคิดเห็นอย่างแน่นอน หากฉันบรรจุคุณลักษณะทุกอย่างที่มีบางอย่างเกี่ยวกับผลิตภัณฑ์ไว้ใน Product ในไม่ช้ามันก็จะกระจัดกระจาย
(OCP) ฉันต้องเปลี่ยนคลาสผลิตภัณฑ์เพื่อขยายคุณสมบัตินี้ ฉันคิดว่านี่เป็นการละเมิดส่วนของ 'ปิดเพื่อการเปลี่ยนแปลง' ของหลักการ ก่อนที่ฉันจะได้รับคำขอของลูกค้าสำหรับการดำเนินการตรวจสอบฉันถือว่าผลิตภัณฑ์เสร็จแล้วและ "ปิด"
มีความสำคัญมากกว่าอะไร: ปฏิบัติตามหลักการของ SOLID หรือมีส่วนต่อประสานที่เรียบง่ายกว่า
หรือฉันกำลังทำอะไรผิดพลาดที่นี่โดยสิ้นเชิง?
ผลลัพธ์
ว้าวขอบคุณสำหรับคำตอบที่ดีของคุณ! เป็นการยากที่จะเลือกคำตอบเป็นทางการ
ให้ฉันสรุปข้อโต้แย้งหลักจากคำตอบ:
- pro (A): OCP ไม่ใช่กฎหมายและความสามารถในการอ่านของรหัสก็มีความสำคัญเช่นกัน
- pro (A): ความสัมพันธ์เอนทิตีควรจะสำรวจได้ ทั้งสองคลาสอาจรู้เกี่ยวกับความสัมพันธ์นี้
- โปร (A) + (B): ทำทั้งสองอย่างและมอบหมายใน (A) ถึง (B) ดังนั้นผลิตภัณฑ์จึงมีโอกาสน้อยที่จะเปลี่ยนอีกครั้ง
- pro (C): ใส่วิธีการค้นหาเข้าไปในคลาสที่สาม (บริการ) ที่ไม่คงที่
- contra (B): ขัดขวางการเยาะเย้ยในการทดสอบ
มีบางสิ่งเพิ่มเติมที่วิทยาลัยของฉันทำงานอยู่:
- โปร (B): กรอบ ORM ของเราสามารถสร้างรหัสสำหรับ (B) ได้โดยอัตโนมัติ
- pro (A): ด้วยเหตุผลทางเทคนิคของกรอบ ORM ของเราเราจำเป็นต้องเปลี่ยนเอนทิตี "ปิด" ในบางกรณีโดยไม่ขึ้นกับที่ตัวค้นหาไป ดังนั้นฉันจะไม่สามารถยึดติดกับ SOLID ได้ตลอดไป
- ตรงกันข้าม (C): เพื่อเอะอะมาก ;-)
ข้อสรุป
ฉันใช้ทั้ง (A) + (B) กับการมอบหมายงานสำหรับโครงการปัจจุบันของฉัน ในสภาพแวดล้อมที่มุ่งเน้นบริการอย่างไรก็ตามฉันจะไปกับ (C)
Assert(5 = Math.Abs(-5));
Abs()
ไม่ใช่ปัญหา แต่เป็นการทดสอบสิ่งที่ขึ้นอยู่กับมัน คุณไม่มีรอยต่อสำหรับการแยก Code-Under-Test (CUT) เพื่อแยกการใช้งานจำลอง ซึ่งหมายความว่าคุณไม่สามารถทดสอบในฐานะหน่วยอะตอมและการทดสอบทั้งหมดของคุณเป็นการทดสอบการรวมที่ตรรกะการทดสอบหน่วย ความล้มเหลวในการทดสอบอาจอยู่ใน CUT หรือในAbs()
(หรือรหัสที่ขึ้นต่อกัน) และลบประโยชน์การวินิจฉัยของการทดสอบหน่วย