ออกแบบ: วิธีการวัตถุกับวิธีการแยกชั้นซึ่งใช้วัตถุเป็นพารามิเตอร์?


14

ตัวอย่างเช่นจะดีกว่าที่จะทำ:

Pdf pdf = new Pdf();
pdf.Print();

หรือ:

Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);

ตัวอย่างอื่น:

Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();

หรือ:

Country m = new Country("Mexico");
Country us = new Country("US");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(us);
double mRatio = ds.GetDebtToGDPRatio(m);    

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

เช่น

Country m = new Country("Mexico");
double ratio = m.GetGDPToMedianIncomeRatio();

อัตราส่วนเหล่านี้ง่าย แต่ให้ถือว่าสถิตินั้นซับซ้อนพอที่จะรับประกันวิธีการ

เส้นแบ่งระหว่างการดำเนินการที่อยู่ภายในกับวัตถุ vs การดำเนินการที่สามารถทำได้บนวัตถุ แต่ไม่ได้เป็นส่วนหนึ่งของอะไร

คำตอบ:


16

ยกตัวอย่าง PDF ของคุณเป็นจุดเริ่มต้นมาดูกันดีกว่า

http://en.wikipedia.org/wiki/Single_responsibility_principle

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

http://en.wikipedia.org/wiki/Separation_of_concerns

หลักการแยกข้อกังวลบอกเราว่าคลาสไม่ควรมีฟังก์ชันที่ทับซ้อนกัน

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

ตอนนี้ในตัวอย่าง PDF ของคุณคำถามคือใครรับผิดชอบการพิมพ์ มีเหตุผลอะไร

ข้อมูลโค้ดแรก:

Pdf pdf = new Pdf();
pdf.Print();

มันไม่ดี เอกสาร PDF ไม่พิมพ์ตัวเอง มันพิมพ์โดย ... ta da! .. เครื่องพิมพ์ ดังนั้นข้อมูลโค้ดที่สองของคุณดีกว่ามาก:

Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);

มันสมเหตุสมผลแล้ว เครื่องพิมพ์ Pdf พิมพ์เอกสาร PDF ดีกว่าเครื่องพิมพ์ไม่ควรเป็นเครื่องพิมพ์ PDF หรือเครื่องพิมพ์ภาพถ่าย ควรเป็นเครื่องพิมพ์ที่สามารถพิมพ์สิ่งที่ส่งไปยังความสามารถที่ดีที่สุด

Pdf pdf = new Pdf();
Printer printer = new Printer();
printer.Print(pdf);

นั่นง่ายมาก วางวิธีการที่เหมาะสม เห็นได้ชัดว่ามันไม่ง่ายเสมอไป ใช้สถิติประเทศของคุณเช่น:

Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();

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

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

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

หากคุณมีสถิติจำนวนมากมาย / หลายตัวแปรตัวอย่างที่สองของคุณก็สมเหตุสมผลดีกว่า:

Country m = new Country("Mexico");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(m);

ยังดีกว่ามี abstract superclass หรือส่วนต่อประสานที่เรียกว่าสถิติที่ใช้ชื่อประเทศเป็นพารามิเตอร์:

interface StatisticsCalculator // or a pure abstract class if doing C++
{
   double getStatistics(Country country); // or a pure virtual function if in C++
}

DebtToGDPRatioStatisticsCalculator ระดับชั้นใช้เครื่องมือ StatisticsCalculator ....

คลาส InfantMortalityStatisticsCalculator ใช้ StatisticsCalculator ...

และอื่น ๆ เป็นต้น ซึ่งนำไปสู่การต่อไปนี้: ลักษณะทั่วไป, การมอบหมาย, สิ่งที่เป็นนามธรรม รวบรวมสถิติได้รับมอบหมายกรณีเฉพาะที่คุยเฉพาะนามธรรม (สถิติคอลเลกชัน API)

ฉันไม่รู้ว่านี่จะตอบคำถามของคุณ 100% หรือไม่ ท้ายที่สุดเราไม่ได้มีโมเดลที่ไม่มีข้อผิดพลาดในกฎหมายที่ขัดขืนไม่ได้ (เช่น EE folks ทำ) สิ่งที่คุณทำได้คือใส่สิ่งที่พวกเขาเข้าท่า และนั่นคือการตัดสินใจทางวิศวกรรมที่คุณต้องทำ สิ่งที่ดีที่สุดที่ต้องทำคือทำความคุ้นเคยกับหลักการ OO (และหลักการสร้างแบบจำลองซอฟต์แวร์ที่ดีโดยทั่วไป)


1
+1 สำหรับส่วนติดต่อ StatisticsCalculator (และการใช้รูปแบบกลยุทธ์ในภายหลัง) และคำตอบที่คิดออกมาอย่างดี
edwardsmatt

3
เวลาไม่เพียงพอที่จะแยกแยะสิ่งนี้อย่างถี่ถ้วนในขณะนี้ แต่ต้องชี้ให้เห็นว่าคลาสเครื่องพิมพ์จะกลายเป็นคลาสพระเจ้าเมื่อเวลาผ่านไปควบคู่ไปกับคลาสเอกสารทุกประเภทอย่างแน่นหนา Pdf.Print น่าจะดีกว่า - แต่ทุกอย่างขึ้นอยู่กับว่าคุณกำหนด 'single responsibility' ;-) อย่างไร
Steven A. Lowe

@ Steve - สิ่งที่คุณเสนอคือความคิดที่น่ากลัว (มี Pdf ใช้งานสิ่งพิมพ์ ()) ไม่ได้สะท้อนถึงการใช้งานการพิมพ์ในชีวิตจริง ทุกระบบปฏิบัติการและ API Printerพิมพ์ว่าฉันปิดให้ตระหนักถึงสิ่งที่เป็นนามธรรมสำหรับ ดูรายการเครื่องพิมพ์ในเครื่อง XP / Vista ของคุณ (หรือภายใต้ / var / spool หรือเทียบเท่าใน * nix) แต่ละแอปพลิเคชันจะทำให้วัตถุเอกสารเป็นอนุกรมกับเครื่องพิมพ์ใดเครื่องพิมพ์หนึ่ง ไม่มีเครื่องพิมพ์ Word หรือเครื่องพิมพ์ข้อความหรือเครื่องพิมพ์ PDF มีเครื่องพิมพ์เฉพาะสำหรับอุปกรณ์การพิมพ์และไม่เฉพาะกับประเภทเอกสาร
luis.espinal

2
+1 ฉันชอบมันฉันไตร่ตรองในสิ่งที่คุณพูด .. @Steve และ luis: ฉันคิดว่าชิ้นส่วนที่ขาดหายไปของการถกเถียงเกี่ยวกับวัตถุพระเจ้าคือวัตถุเครื่องพิมพ์ทั่วไปควรยอมรับรูปแบบมาตรฐานบางอย่างเช่น ASCII หรือบิตแมป (ถึง pdf ก็มีเหตุผลด้วยเช่นกัน) และควรเป็นหน้าที่ของคลาสที่ 3 ในการแปลงประเภทเอกสารเฉพาะ (กล่าวคำว่า ms word document) เป็นหนึ่งในรูปแบบมาตรฐานเหล่านี้
ผู้ใช้

2
สำหรับฉันแล้วดูเหมือนว่า PDF อาจจะสามารถแสดงผลตัวเองลงบนส่วนต่อประสาน Canvas หรือวัตถุรูปภาพซึ่งสามารถประมวลผลโดยวัตถุเครื่องพิมพ์ได้
Winston Ewert

4

ฉันคิดว่าคงไม่ดีไปกว่าอีกแน่นอน การใช้ pdf.Print () นั้นเข้มงวดมากขึ้น แต่การมีคลาส PdfPrinter อาจดีกว่าถ้า:

  • คุณต้องจัดการอินสแตนซ์ของเครื่องพิมพ์
  • มีตัวเลือกและการกระทำที่หลากหลายที่จะทำให้ความซับซ้อนของ pdf.Print (... ) (เช่นการยกเลิกการพิมพ์การจัดรูปแบบพิเศษ ฯลฯ )

ฉันจะไม่ถูกแขวนไว้กับมันเป็นอย่างอื่น


คำตอบที่ดีและใช้งานได้จริง; เวลาจะบอกว่าสิ่งนี้จำเป็นต้องพัฒนาอย่างไร
Steven A. Lowe

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