ของแข็งกับวิธีคงที่


11

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

(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)


2
ตราบใดที่มันไม่ใช่ตัวแปรคงที่ทุกอย่างก็เจ๋ง วิธีการคงที่ง่ายต่อการทดสอบและง่ายต่อการติดตาม
Coder

3
ทำไมไม่เพียงแค่มีคลาส ProductsReviews จากนั้นสินค้าและรีวิวยังคงเหมือนเดิม หรือฉันอาจเข้าใจผิด
ElGringoGrande

2
@Coder "วิธีการคงที่ง่ายต่อการทดสอบ" จริงเหรอ? พวกเขาไม่สามารถล้อเลียนได้ดู: googletesting.blogspot.com/2008/12/…สำหรับรายละเอียดเพิ่มเติม
StuperUser

1
@StuperUser: ไม่มีอะไรที่จะเยาะเย้ย Assert(5 = Math.Abs(-5));
Coder

2
การทดสอบAbs()ไม่ใช่ปัญหา แต่เป็นการทดสอบสิ่งที่ขึ้นอยู่กับมัน คุณไม่มีรอยต่อสำหรับการแยก Code-Under-Test (CUT) เพื่อแยกการใช้งานจำลอง ซึ่งหมายความว่าคุณไม่สามารถทดสอบในฐานะหน่วยอะตอมและการทดสอบทั้งหมดของคุณเป็นการทดสอบการรวมที่ตรรกะการทดสอบหน่วย ความล้มเหลวในการทดสอบอาจอยู่ใน CUT หรือในAbs()(หรือรหัสที่ขึ้นต่อกัน) และลบประโยชน์การวินิจฉัยของการทดสอบหน่วย
StuperUser

คำตอบ:


7

มีความสำคัญมากกว่าอะไร: ปฏิบัติตามหลักการของ SOLID หรือมีส่วนต่อประสานที่เรียบง่ายกว่า

อินเตอร์เฟส vis-a-vis SOLID

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

หลักการเปิด / ปิด

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

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

ให้ภาพที่ดีที่สุดของคุณเรามีทีมแพทย์ที่คอยดูแล

หากการวิเคราะห์ความต้องการของคุณบอกคุณว่าคุณต้องแสดงความสัมพันธ์ของการทบทวนผลิตภัณฑ์จากมุมมองทั้งสองให้ทำเช่นนั้น

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


1
+1 สำหรับการสังเกตว่า OCP เป็นตัวบ่งชี้คุณภาพดีมากกว่าไม่ใช่กฎที่รวดเร็วสำหรับรหัสใด ๆ หากคุณพบว่าเป็นการยากหรือเป็นไปไม่ได้ที่จะปฏิบัติตาม OCP อย่างถูกต้องนั่นเป็นสัญญาณว่าแบบจำลองของคุณต้องได้รับการปรับโครงสร้างใหม่เพื่อให้สามารถใช้ซ้ำได้อย่างยืดหยุ่นมากขึ้น
CodexArcanum

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

10

โซลิดเป็นแนวทางดังนั้นมีอิทธิพลต่อการตัดสินใจมากกว่ากำหนด

สิ่งหนึ่งที่ต้องระวังเมื่อใช้วิธีการแบบคงที่เป็นผลกระทบต่อการตรวจสอบ


การทดสอบforProduct(Product product)จะไม่มีปัญหา

การทดสอบสิ่งที่ขึ้นอยู่กับมันจะเป็น

คุณจะไม่มีรอยต่อสำหรับการแยก Code-Under-Test (CUT) ที่ต้องพึ่งพาเพื่อแยกจำลองเนื่องจากเมื่อแอปพลิเคชันทำงานอยู่วิธีการคงที่จำเป็นต้องมีอยู่

เรามีวิธีที่เรียกCUT()ว่าการโทรforProduct()

ถ้าforProduct()เป็นstaticคุณจะไม่สามารถทดสอบCUT()เป็นหน่วยอะตอมและทั้งหมดของการทดสอบของคุณกลายเป็นว่าการทดสอบการรวมตรรกะหน่วยทดสอบ

ความล้มเหลวในการทดสอบ CUT อาจเกิดจากปัญหาในCUT()หรือในforProduct()(หรือรหัสที่ขึ้นต่อกันใด ๆ ) ซึ่งจะลบประโยชน์การวินิจฉัยของการทดสอบหน่วย

ดูโพสต์บล็อกที่ยอดเยี่ยมนี้สำหรับข้อมูลรายละเอียดเพิ่มเติม: http://googletesting.blogspot.com/2008/12/static-methods-are-death-to-testability.html


สิ่งนี้สามารถนำไปสู่ความไม่พอใจกับการทดสอบที่ล้มเหลวและการละทิ้งแนวทางปฏิบัติที่ดีและผลประโยชน์ที่ล้อมรอบพวกเขา


1
นั่นเป็นจุดที่ดีมาก โดยทั่วไปแล้วไม่ใช่สำหรับฉันแม้ว่า ;-) ฉันไม่เข้มงวดกับการเขียนการทดสอบหน่วยที่ครอบคลุมมากกว่าหนึ่งคลาส พวกเขาทั้งหมดลงไปที่ฐานข้อมูลนั่นไม่เป็นไรสำหรับฉัน ดังนั้นฉันจะไม่จำลองวัตถุทางธุรกิจหรือเป็นตัวค้นหา
Wolfgang

+1 ขอบคุณสำหรับการตั้งค่าสถานะสีแดงสำหรับการทดสอบ! ฉันเห็นด้วยกับ @ Wolfgang ปกติฉันก็ไม่เข้มงวดเหมือนกัน แต่เมื่อฉันต้องการการทดสอบแบบนั้นฉันเกลียดวิธีการคงที่ โดยปกติถ้าวิธีการคงที่โต้ตอบมากเกินไปกับพารามิเตอร์ของมันหรือถ้าโต้ตอบกับวิธีคงที่อื่น ๆ ฉันชอบที่จะทำให้มันเป็นวิธีการอินสแตนซ์
Adriano Repetti

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

1
@StuperUser If forProduct() is static you can't test CUT() as an atomic unit and all of your tests become integration tests that test unit logic.- ฉันเชื่อว่า Javascript และ Python ทั้งสองอนุญาตวิธีการแบบคงที่จะถูกแทนที่ / เยาะเย้ยออก แม้ว่าฉันจะไม่แน่ใจ 100%
Izkata

1
@Izkata JS ถูกพิมพ์แบบไดนามิกดังนั้นจึงไม่มีstaticคุณจำลองด้วยการปิดและรูปแบบซิงเกิล การอ่านบน Python (โดยเฉพาะอย่างยิ่งstackoverflow.com/questions/893015/… ) คุณต้องรับช่วงต่อและขยาย การเอาชนะไม่ได้เป็นการเยาะเย้ย ดูเหมือนว่าคุณยังไม่มีรอยต่อสำหรับการทดสอบรหัสเป็นหน่วยอะตอม
StuperUser

4

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

ตัวอย่างเช่นฉันจะถูกล่อลวงให้ฉีดบางสิ่งบางอย่างที่เล่นบทบาทของทบทวนทบทวน IRetrieveReviewsผมก็อาจจะให้อินเตอร์เฟซ คุณสามารถใส่สิ่งนี้ลงใน Constructor ของผลิตภัณฑ์ (Dependency Injection) หากคุณต้องการที่จะเปลี่ยนวิธีการความคิดเห็นจะถูกเรียกคุณสามารถทำมันได้อย่างง่ายดายโดยการฉีดสมรู้ร่วมคิดที่แตกต่างกัน - เป็นTwitterReviewRetrieverหรือAmazonReviewRetrieverหรือMultipleSourceReviewRetrieverหรือสิ่งอื่นที่คุณต้องการ

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


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

1
การใช้อินเทอร์เฟซอย่างIRetrieveReviewsหยุดยั้งมันคือการให้บริการ - ไม่ได้กำหนดสิ่งที่ได้รับความเห็นหรือวิธีการหรือเวลา อาจเป็นบริการที่มีวิธีการมากมายสำหรับสิ่งนี้ อาจเป็นคลาสที่ทำสิ่งนั้น อาจเป็นที่เก็บหรือร้องขอ HTTP ไปยังเซิร์ฟเวอร์ คุณไม่รู้ คุณไม่ควรรู้ นั่นคือประเด็น
Lunivore

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

3

ฉันจะมีคลาส ProductsReview คุณบอกว่ารีวิวเป็นของใหม่อยู่ดี ไม่ได้หมายความว่ามันจะเป็นอะไรก็ได้ มันยังคงต้องมีเหตุผลเดียวที่จะเปลี่ยน หากคุณเปลี่ยนวิธีรับความเห็นไม่ว่าด้วยเหตุผลใดก็ตามคุณจะต้องเปลี่ยนระดับความเห็น

ไม่ถูกต้อง

คุณกำลังวางวิธีแบบคงที่ในชั้นเรียนเพราะ ... เพราะอะไร นั่นคือสิ่งที่คุณกำลังดิ้นรนกับ? นั่นไม่ใช่ปัญหาทั้งหมดหรือ

จากนั้นทำไม่ได้ ทำคลาสที่รับผิดชอบ แต่เพียงผู้เดียวคือการรีวิวผลิตภัณฑ์ จากนั้นคุณสามารถแบ่งคลาสย่อยเป็น ProductReviewsByStartRating ได้ หรือ subclass เพื่อรับความเห็นต่อคลาสของผลิตภัณฑ์


ฉันไม่เห็นด้วยว่าการมีวิธีการตรวจสอบจะเป็นการละเมิด SRP เกี่ยวกับผลิตภัณฑ์มันจะ แต่ไม่ได้อยู่ในรีวิว ปัญหาของฉันคือมันคงที่ ถ้าฉันย้ายเมธอดไปยังคลาสที่สามมันจะยังคงเป็นแบบสแตติกและมีพารามิเตอร์ผลิตภัณฑ์
Wolfgang

ดังนั้นคุณกำลังบอกว่าความรับผิดชอบเพียงอย่างเดียวสำหรับคลาสรีวิวเหตุผลเดียวที่ทำให้มันเปลี่ยนแปลงคือถ้าต้องการวิธีแบบคงที่ผลิตภัณฑ์สำหรับการเปลี่ยนแปลงหรือไม่ ไม่มีฟังก์ชั่นอื่น ๆ ในชั้นเรียนรีวิวหรือไม่
ElGringoGrande

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

3

ฉันจะไม่ใส่ฟังก์ชั่น 'รับรีวิวผลิตภัณฑ์' ทั้งในProductห้องเรียนหรือในReviewชั้นเรียน ...

คุณมีสถานที่ที่คุณค้นคืนผลิตภัณฑ์ใช่ไหม? บางสิ่งบางอย่างที่มีGetProductById(int productId)และอาจจะGetProductsByCategory(int categoryId)และอื่น ๆ

ในทำนองเดียวกันคุณควรจะมีสถานที่ที่จะดึงความคิดเห็นของคุณด้วยและอาจจะGetReviewbyId(int reviewId)GetReviewsForProduct(int productId)

เมื่อฉันต้องการเปลี่ยนวิธีการรวบรวมความเห็นสำหรับผลิตภัณฑ์ฉันต้องเปลี่ยนระดับผลิตภัณฑ์

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


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

@CodexArcanum ดีคุณไม่ได้อยู่คนเดียว :-)
Eric King

2

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

ในกรณีนี้ฉันต้องการโซลูชัน (B) เพราะสำหรับฉัน จุดเริ่มต้นคือผลิตภัณฑ์ไม่ใช่รีวิว แต่คิดว่าคุณเขียนซอฟต์แวร์เพื่อจัดการคำวิจารณ์ ในกรณีนี้ศูนย์กลางคือการตรวจสอบดังนั้นการแก้ปัญหา (A) อาจจะดีกว่า

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


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

ฉันคิดว่าสำหรับชั้นเรียนขนาดเล็กมันอาจจะดีแม้จะมีวิธีการทั้งสอง (A) และ (B) มีหลายครั้งที่ UI ไม่เป็นที่รู้จักในขณะเขียนตรรกะนี้
Adriano Repetti

1

ผลิตภัณฑ์ของคุณสามารถมอบให้กับวิธีการตรวจสอบแบบคงที่ของคุณซึ่งในกรณีนี้คุณได้จัดเตรียมอินเทอร์เฟซที่สะดวกสบายในตำแหน่งที่เป็นธรรมชาติ (Product.getReviews) แต่รายละเอียดการใช้งานของคุณอยู่ใน Review.getForProduct

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


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

กุญแจสำคัญของ OCP คือคุณสามารถแทนที่การใช้งานได้โดยไม่ต้องเปลี่ยนรหัสของคุณ ในทางปฏิบัติสิ่งนี้มีความเกี่ยวข้องอย่างใกล้ชิดกับการทดแทน Liskov การใช้งานอย่างหนึ่งจะต้องทดแทนกันได้ คุณอาจมี DatabaseReviews และ SoapReviews เป็นคลาสที่ต่างกันทั้งการใช้ IGetReviews.getReviews และ getReviewsForProducts จากนั้นระบบของคุณจะเปิดเพื่อขยาย (เปลี่ยนวิธีการตรวจสอบความคิดเห็น) และปิดเพื่อการปรับเปลี่ยน (การพึ่งพา IGetReviews จะไม่แตก) นั่นคือสิ่งที่ฉันหมายถึงโดยการจัดการการพึ่งพา ในกรณีของคุณคุณจะต้องแก้ไขรหัสเพื่อเปลี่ยนพฤติกรรม
pfries

นั่นไม่ใช่หลักการพึ่งพาการพึ่งพา (DIP) ที่คุณกำลังอธิบาย?
Wolfgang

พวกมันเกี่ยวข้องกับหลักการ จุดทดแทนของคุณได้มาจากกรมทรัพย์สินทางปัญญา
pfries

1

ฉันมีสิ่งที่แตกต่างออกไปเล็กน้อยจากคำตอบส่วนใหญ่ ฉันคิดว่าProductและReviewเป็นวัตถุถ่ายโอนข้อมูล (DTO) ในรหัสของฉันฉันพยายามทำให้ DTOs / เอนทิตีของฉันหลีกเลี่ยงพฤติกรรม พวกเขาเป็นเพียง API ที่ดีในการจัดเก็บสถานะปัจจุบันของโมเดลของฉัน

เมื่อคุณพูดถึง OO และ SOLID โดยทั่วไปคุณกำลังพูดถึง "วัตถุ" ที่ไม่ได้เป็นตัวแทนของรัฐ (จำเป็น) แต่เป็นตัวแทนบริการบางประเภทที่ตอบคำถามให้คุณหรือให้คุณมอบหมายงานบางส่วนของคุณ . ตัวอย่างเช่น

interface IProductRepository
{
    void SaveNewProduct(IProduct product);
    IProduct GetProductById(ProductId productId);
    bool TryGetProductByName(string name, out IProduct product);
}

interface IProduct
{
    ProductId Id { get; }
    string Name { get; }
}

class ExistingProduct : IProduct
{
    public ProductId Id { get; private set; }
    public string Name { get; private set; }
}

จากนั้นจริงของคุณProductRepositoryจะส่งกลับExistingProductสำหรับGetProductByProductIdวิธีการ ฯลฯ

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

หากคุณเปลี่ยนคีมาฐานข้อมูลของคุณคุณสามารถเปลี่ยนการใช้พื้นที่เก็บข้อมูลของคุณโดยไม่ต้องเปลี่ยน DTO ของคุณ ฯลฯ

ดังนั้นในระยะสั้นฉันคิดว่าฉันจะไม่เลือกตัวเลือกของคุณ :)


0

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

ทำให้ทั้งผลิตภัณฑ์ GetReviews และ Review.ForProduct วิธีการหนึ่งบรรทัดที่มีลักษณะดังนี้

new ReviewService().GetReviews(productID);

ReviewService มีรหัสที่ซับซ้อนมากขึ้นและมีอินเตอร์เฟซที่ออกแบบมาสำหรับการตรวจสอบได้ แต่ไม่ได้สัมผัสกับผู้ใช้โดยตรง

หากคุณต้องมีการครอบคลุม 100% ให้ทำการทดสอบการรวมระบบของคุณเพื่อเรียกวิธีการเรียนของผลิตภัณฑ์ / บทวิจารณ์

มันอาจช่วยถ้าคุณคิดถึงการออกแบบ API สาธารณะแทนที่จะออกแบบคลาส - ในบริบทนั้นอินเตอร์เฟสแบบเรียบง่ายพร้อมการจัดกลุ่มแบบลอจิคัลคือทุกสิ่งที่สำคัญ - โครงสร้างรหัสจริงมีความสำคัญต่อนักพัฒนาเท่านั้น

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