รหัสใดควรรวมอยู่ในคลาสนามธรรม?


10

เมื่อเร็ว ๆ นี้ฉันมีปัญหาเกี่ยวกับการใช้คลาสที่เป็นนามธรรม

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

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

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

รหัสที่อาจไม่มีความหมายสำหรับคลาสนามธรรมควรมีเพียงเพราะมันเกิดขึ้นเป็นเรื่องธรรมดาสำหรับคลาสที่ได้รับ?

ขอยกตัวอย่าง: คลาสนามธรรม A มีเมธอด a () และเมธอด abstract aq () เมธอด aq () ทั้งคลาสที่ได้รับ AB และ AC ใช้เมธอด b () ควรย้าย b () ไปที่ A หรือไม่ ถ้าใช่แล้วในกรณีที่มีคนดูที่ A (สมมติว่า AB และ AC ไม่อยู่ที่นั่น) การมีอยู่ของ b () จะไม่สมเหตุสมผล! นี่เป็นสิ่งที่ไม่ดีเหรอ? ควรมีใครบางคนสามารถมองในชั้นนามธรรมและเข้าใจสิ่งที่เกิดขึ้นโดยไม่ต้องไปเรียนที่ได้รับ?

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

คุณคิด / ฝึกทำอะไร


7
ทำไมต้องมี "กฎ"
Robert Harvey

1
Writing an abstract class that makes sense without having to look in the derived classes is a matter of clean code and clean architecture.- ทำไม เป็นไปไม่ได้ที่ในระหว่างการออกแบบและพัฒนาแอพพลิเคชั่นฉันพบว่ามีหลายคลาสที่มีฟังก์ชั่นการใช้งานทั่วไป ฉันต้องมีญาณทิพย์เพียงพอที่จะคาดหวังสิ่งนี้เสมอก่อนที่จะเขียนโค้ดใด ๆ สำหรับคลาสที่ได้รับ หากฉันล้มเหลวในการเป็นคนฉลาดนี้ฉันจะถูกห้ามไม่ให้ทำการปรับโครงสร้างดังกล่าวอีกครั้งหรือไม่? ฉันต้องโยนรหัสของฉันและเริ่มต้นใหม่หรือไม่
Robert Harvey

ขออภัยถ้าฉันเข้าใจผิด! ฉันพยายามที่จะพูดในสิ่งที่ฉันรู้สึก (ในขณะนี้) เพื่อเป็นการฝึกฝนที่ดีกว่าและไม่ได้หมายความว่าควรมีกฎที่สมบูรณ์ ยิ่งกว่านั้นฉันไม่ได้หมายความว่าโค้ดใด ๆ ที่เป็นของคลาสนามธรรมควรเขียนล่วงหน้าเท่านั้น ฉันอธิบายว่าในทางปฏิบัติคลาสนามธรรมจะลงท้ายด้วยโค้ดระดับสูง (ซึ่งทำหน้าที่เป็นเทมเพลตสำหรับสิ่งที่ได้รับ) และโค้ดระดับต่ำ คลาสที่ได้รับ)
Alexandros Gougousis

@RobertHarvey สำหรับคลาสพื้นฐานสาธารณะคุณจะถูกห้ามไม่ให้ดูคลาสที่ได้รับ สำหรับคลาสภายในนั้นไม่สร้างความแตกต่าง
Frank Hileman

คำตอบ:


4

ขอยกตัวอย่าง: คลาสนามธรรม A มีเมธอด a () และเมธอด abstract aq () เมธอด aq () ทั้งคลาสที่ได้รับ AB และ AC ใช้เมธอด b () ควรย้าย b () ไปที่ A หรือไม่ ถ้าใช่แล้วในกรณีที่มีคนดูที่ A (สมมติว่า AB และ AC ไม่อยู่ที่นั่น) การมีอยู่ของ b () จะไม่สมเหตุสมผล! นี่เป็นสิ่งที่ไม่ดีเหรอ? ควรมีใครบางคนสามารถมองในชั้นนามธรรมและเข้าใจสิ่งที่เกิดขึ้นโดยไม่ต้องไปเรียนที่ได้รับ?

สิ่งที่คุณถามคือสถานที่b()และในบางแง่มุมคำถามคือว่าAเป็นตัวเลือกที่ดีที่สุดในชั้นเรียนพิเศษสำหรับABหรือACไม่

ดูเหมือนว่ามีสามตัวเลือก:

  1. ปล่อยให้b()ทั้งABและAC
  2. สร้างคลาสกลางABAC-Parentที่สืบทอดจากAและที่แนะนำb()จากนั้นจะใช้เป็นคลาสพิเศษทันทีสำหรับABและAC
  3. ใส่b()ในA(ไม่ทราบว่าในอนาคตอีกชั้นADจะต้องการb()หรือไม่)

  1. ทนทุกข์ทรมานจากการไม่แห้ง
  2. ทนทุกข์ทรมานจากYAGNI
  3. ดังนั้นมันจะทิ้งสิ่งนี้

จนกว่าจะมีคลาสอื่นADที่ไม่ต้องการb()นำเสนอตัวเอง (3) ดูเหมือนจะเป็นทางเลือกที่เหมาะสม

ในเวลาดังกล่าวเป็นADของขวัญจากนั้นเราสามารถ refactor วิธีการใน (2) - หลังจากทั้งหมดมันเป็นซอฟต์แวร์!


7
มีตัวเลือกที่ 4 อย่าใส่b()ในคลาสใด ๆ ทำให้เป็นฟังก์ชั่นฟรีที่นำข้อมูลทั้งหมดมาเป็นอาร์กิวเมนต์และมีทั้งคู่ABและACเรียกมัน ADซึ่งหมายความว่าคุณทำไม่ได้ต้องย้ายหรือสร้างชั้นเรียนมากขึ้นเมื่อคุณเพิ่ม
user1118321

1
@ user1118321 ยอดเยี่ยมดีคิดนอกกรอบ เหมาะสมเป็นพิเศษหากb()ไม่ต้องการสภาวะที่มีอายุยืนยาว
Erik Eidt

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

คุณสามารถสร้างเบส / ค่าเริ่มต้นacที่เรียกbแทนการacเป็นนามธรรม
Erik Eidt

3
สำหรับฉันคำถามที่สำคัญที่สุดคือทำไมทั้ง AB และ AC ใช้วิธีเดียวกัน b () ถ้ามันเป็นเรื่องบังเอิญฉันจะทิ้งการใช้งานสองอย่างที่คล้ายกันไว้ใน AB และ AC แต่อาจเป็นเพราะมีบางสิ่งที่เป็นนามธรรมสำหรับ AB และ AC สำหรับฉันแล้ว DRY ไม่ได้มีคุณค่าในตัวเองมากนัก แต่เป็นคำใบ้ว่าคุณอาจจะคิดถึงสิ่งที่เป็นประโยชน์
Ralf Kleberhoff

2

คลาสนามธรรมไม่ได้หมายถึงพื้นทิ้งสำหรับฟังก์ชั่นหรือข้อมูลต่าง ๆ ที่ถูกโยนเข้าไปในชั้นนามธรรมเพราะสะดวก

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

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

ความคิดของคลาสนามธรรมคือการมีแบบจำลองนามธรรมของคลาสที่ได้รับซึ่งใช้คลาสนามธรรมจริง ๆ ควรให้บริการหรือพฤติกรรม การสืบทอดนั้นง่ายต่อการใช้งานและหลายครั้งที่คลาสที่มีประโยชน์ที่สุดประกอบด้วยคลาสต่างๆโดยใช้รูปแบบมิกซ์อิน

อย่างไรก็ตามมีคำถามการเปลี่ยนแปลงอยู่เสมอสิ่งที่จะเปลี่ยนแปลงและจะเปลี่ยนแปลงอย่างไรและทำไมจึงเปลี่ยน

การสืบทอดสามารถนำไปสู่แหล่งที่มาที่เปราะและเปราะบาง (ดูการสืบทอด: เพียงหยุดใช้มัน! )

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


ฉันแน่ใจว่าแนวคิด OOP ใด ๆ สามารถนำไปสู่ปัญหาได้หากมันถูกใช้ในทางที่ผิด ยิ่งกว่านั้นการตั้งสมมติฐานว่า b () เป็นวิธีการห้องสมุด (เช่นฟังก์ชั่นที่บริสุทธิ์โดยไม่มีการอ้างอิงอื่น ๆ ) ทำให้ขอบเขตของคำถามของฉันแคบลง เรามาหลีกเลี่ยงสิ่งนี้กัน
Alexandros Gougousis

@AlexandrosGougousis ฉันไม่ได้สมมติว่าb()เป็นฟังก์ชั่นที่บริสุทธิ์ มันอาจเป็น functoid หรือแม่แบบหรืออย่างอื่น ฉันใช้วลี "วิธีห้องสมุด" เป็นความหมายขององค์ประกอบบางอย่างไม่ว่าจะเป็นฟังก์ชั่นแท้ ๆ , วัตถุ COM หรืออะไรก็ตามที่ใช้สำหรับฟังก์ชั่นการใช้งานที่ให้ไว้กับโซลูชัน
Richard Chambers

ขออภัยถ้าฉันยังไม่ชัดเจน! ฉันพูดถึงฟังก์ชั่นที่บริสุทธิ์ว่าเป็นหนึ่งในหลาย ๆ ตัวอย่าง (คุณให้มากกว่านี้)
Alexandros Gougousis

1

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

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

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

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

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

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

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

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

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

คลาสนามธรรมที่ดีจะลดจำนวนของรหัสที่ต้องเขียนใหม่เนื่องจากสามารถใช้งานร่วมกันได้หรือรัฐ


ฉันจะยอมรับว่า s () การเป็นส่วนหนึ่งของส่วนต่อประสานสาธารณะทำให้สิ่งต่าง ๆ ยุ่งยากมากขึ้น ฉันมักจะหลีกเลี่ยงการกำหนดวิธีการสาธารณะเรียกวิธีสาธารณะอื่น ๆ ในชั้นเรียนเดียวกัน
Alexandros Gougousis

1

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

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

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

ชื่อคลาสนามธรรมของคุณร่วมกับสมาชิกควรสมเหตุสมผล มันจะต้องเป็นอิสระจากชั้นเรียนใด ๆ ทั้งในทางเทคนิคและมีเหตุผล เช่นเดียวกับสัตว์อาจมีวิธีนามธรรมกิน (ซึ่งจะเป็น polymorphic) และคุณสมบัตินามธรรมแบบบูล IsPet และ IsLifestock ที่เหมาะสมโดยไม่ทราบเกี่ยวกับแมวหรือสุนัขหรือหมู การพึ่งพาใด ๆ (ทางเทคนิคหรือตรรกะ) ควรไปทางเดียวเท่านั้น: จากลูกหลานถึงฐาน คลาสฐานเองไม่ควรมีความรู้ในการสืบทอดคลาสเช่นกัน


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

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

มีอีกอย่างหนึ่งเกี่ยวกับคลาสฐานที่รู้ชนิดย่อยของมัน เมื่อใดก็ตามที่มีการพัฒนาประเภทย่อยใหม่ชั้นฐานจะต้องมีการแก้ไขซึ่งเป็นไปข้างหลังและละเมิดหลักการเปิดปิด ฉันเพิ่งพบสิ่งนี้ในรหัสที่มีอยู่ คลาสพื้นฐานมีวิธีการแบบคงที่ที่สร้างอินสแตนซ์ของชนิดย่อยตามการกำหนดค่าของโปรแกรมประยุกต์ เจตนาคือรูปแบบของโรงงาน การทำเช่นนี้เป็นการละเมิด SRP ด้วย ด้วยคะแนน SOLID บางครั้งฉันเคยคิดว่า "duh นั่นชัดเจน" หรือ "ภาษาจะดูแลโดยอัตโนมัติ" แต่ฉันพบว่าผู้คนมีความคิดสร้างสรรค์มากกว่าที่ฉันจะจินตนาการ
Martin Maat

0

กรณีแรกจากคำถามของคุณ:

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

กรณีที่สอง:

บางครั้ง ... คลาสนามธรรมมักใช้เป็นสถานที่ซึ่งคุณสามารถใส่รหัสทั่วไปใด ๆ ที่คลาสที่ได้รับปัจจุบันมีอยู่

จากนั้นคำถามของคุณ :

ฉันสงสัยว่าทั้งสองกรณีนี้ควรเป็นกฎ ... รหัสที่อาจไม่มีความหมายสำหรับคลาสนามธรรมควรมีเพียงเพราะมันเกิดขึ้นเป็นเรื่องธรรมดาสำหรับคลาสที่ได้รับ?

IMO คุณมีสองสถานการณ์ที่แตกต่างกันดังนั้นคุณไม่สามารถวาดกฎเดียวเพื่อตัดสินใจว่าจะต้องใช้การออกแบบใด

สำหรับกรณีแรกเรามีสิ่งต่าง ๆ เช่นรูปแบบวิธีการของแม่แบบที่กล่าวถึงแล้วในคำตอบอื่น ๆ

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


-1

ฉันมีคำถามเดียวกันทุกครั้งที่ผ่านมา!

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

abstract class AbstractProtocol
{
    /**
     * @return array Registration params to send
     */
    abstract protected function assembleRegistrationPart();

    /**
     * @return array Payment params to send
     */
    abstract protected function assemblePaymentPart();

    protected function doSend(array $data)
    {
        return
            (new HttpClient(
                [
                    'timeout' => 60,
                    'encoding' => 'utf-8',
                    'language' => 'en',
                ]
            ))
                ->send($data);
    }

    protected function log(array $data)
    {
        $header = 'Here is a request to external system!';
        $body = implode(', ', $this->maskData($data));
        Logger::log($header . '. \n ' . $body);
    }
}

class ClassicProtocol extends AbstractProtocol
{
    public function send()
    {
        $registration = $this->assembleRegistrationPart();
        $payment = $this->assemblePaymentPart();
        $specificParams = $this->assembleClassicSpecificPart();

        $dataToSend =
            array_merge(
                $registration, $payment, $specificParams
            );

        $this->log($dataToSend);

        $this->doSend($dataToSend);
    }

    protected function assembleRegistrationPart()
    {
        return ['hello' => 'there'];
    }

    protected function assemblePaymentPart()
    {
        return ['pay' => 'yes'];
    }
}

รหัสดังกล่าวระบุว่าฉันเพียงแค่ใช้มรดกในทางที่ผิด นั่นคือวิธีที่มันสามารถนำกลับมาใช้ใหม่ได้:

class ClassicProtocol
{
    private $request;
    private $logger;

    public function __construct(Request $request, Logger $logger, Client $client)
    {
        $this->request = $request;
        $this->client = $client;
        $this->logger = $logger;
    }

    public function send()
    {
        $this->logger->log($this->request->getData());
        $this->client->send($this->request->getData());
    }
}

$protocol =
    new ClassicProtocol(
        new PaymentRequest(
            new RegistrationData(),
            new PaymentData(),
            new ClassicSpecificData()
        ),
        new ClassicLogger(),
        new ClassicClient()
    );

class RegistrationData
{
    public function getData()
    {
        return ['hello' => 'there'];
    }
}

class PaymentData
{
    public function getData()
    {
        return ['pay' => 'yes'];
    }
}

class ClassicLogger
{
    public function log(array $data)
    {
        $header = 'Here is a request to external system!';
        $body = implode(', ', $this->maskData($data));
        Logger::log($header . '. \n ' . $body);
    }
}
class ClassicClient
{
    private $properties;

    public function __construct()
    {
        $this->properties =
            [
                'timeout' => 60,
                'encoding' => 'utf-8',
                'language' => 'en',
            ];
    }
}

ตั้งแต่นั้นฉันก็ปฏิบัติต่อมรดกอย่างระมัดระวังทำให้ฉันเจ็บหลายครั้ง

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


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