ทำไมการทำคลาสย่อยแย่เกินไป (และด้วยเหตุนี้เราควรใช้ต้นแบบเพื่อทำไป)


10

ฉันอ่านเกี่ยวกับรูปแบบการออกแบบและฉันอ่านว่ารูปแบบการออกแบบต้นแบบนั้นมีซับคลาสที่มากเกินไป

ทำไมการแบ่งคลาสย่อยไม่ดี การใช้ต้นแบบจะได้ประโยชน์อะไรจากการทำ subclassing


พูดว่าใคร คุณมีลิงค์ไหม
camus

ฉันอ่าน p.118 ของหนังสือเล่มนี้ goo.gl/6JGw1
David Faux

คำตอบ:


19

ทำไมการแบ่งคลาสย่อยมากเกินไปจึงไม่ดี

"มากเกินไป" เป็นคำพิพากษา แต่เอาไปจากฉันมันไม่ดี รหัสที่ฉันทำงานด้วยมีการสืบทอด DEEP และรหัสนั้นยากที่จะอ่านทำความเข้าใจติดตามติดตามตรวจแก้จุดบกพร่อง ฯลฯ ฯลฯ เป็นไปไม่ได้ที่จะเขียนโค้ดทดสอบสำหรับสิ่งนี้ได้อย่างมีประสิทธิภาพ

รูปแบบต้นแบบทำไปด้วย subclassing

หรือคำถามนั้นหมายความว่า "มีคลาสย่อยมากเกินไปหรือไม่" รูปแบบนี้เรียกใช้การโคลนวัตถุ "เทมเพลต" เพื่อหลีกเลี่ยงการทำคลาสย่อยอย่างน้อยตอนนี้ ไม่มีกฎที่ระบุว่า "เทมเพลต" ไม่สามารถเป็นคลาสย่อยได้

ชอบองค์ประกอบมากกว่ามรดก

แนวคิดนี้รวมถึงการมอบหมายและการรวม การติดตามความรู้นี้หมายถึงซอฟต์แวร์ของคุณมีความยืดหยุ่นมากขึ้นง่ายต่อการบำรุงรักษาขยายและนำกลับมาใช้ใหม่

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

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

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


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

@alison: วิธีการในคลาสที่ได้รับซึ่งใช้วิธีอื่นจากคลาสฐานที่การใช้งานในคลาสฐานทำให้แทบเป็นไปไม่ได้ที่จะทดสอบสถานการณ์ข้อผิดพลาด หากมีการจัดวางองค์ประกอบแล้วจะเป็นการง่ายกว่าในการฉีดวัตถุที่ทำให้เกิดข้อผิดพลาดขึ้น
Rune FS

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

8

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

ฉันไม่เชื่อเรื่องนี้เลย ดูเหมือนว่าดื้อรั้นที่จะบอกว่ามันไม่ดี มีอะไรไม่ดีเมื่อนำไปใช้ในทางที่ผิด


2

ด้วยเหตุผลสองประการ

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

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


6
-1: คุณพูดถูก # 2; แต่ไม่เกี่ยวกับ # 1 หลายชั้นของมรดกจะไม่จำเป็นต้องส่งผลกระทบต่อประสิทธิภาพการทำงานรันไทม์
Jim G.

กังวลเกี่ยวกับการเรียกขั้นตอนเพิ่มเติมสองสามครั้งที่ระดับรหัสเครื่องและใช้ OP และการสืบทอดและคุณกังวลเกี่ยวกับความมี
ชีวิตชีวา

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

2

"มากเกินไป" เป็นคำพิพากษา

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

ที่กล่าวว่าองค์ประกอบที่นิยมเหนือมรดกเป็นกฎง่ายๆ

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

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

ตัวอย่างหนึ่งที่ฉันนึกได้คือ java.util.Properties ซึ่งสืบทอดมาจาก Hashtable คลาส Properties มีจุดประสงค์เพื่อเก็บคู่ String-String เท่านั้น (ซึ่งจะถูกบันทึกไว้ใน Javadocs) เนื่องจากการสืบทอดวิธีการ put และ putAll จาก Hashtable ได้ถูกเปิดเผยแก่ผู้ใช้ Properties ในขณะที่วิธีการ "ถูกต้อง" ในการจัดเก็บพร็อพเพอร์ตี้คือ setProperty () ไม่มีอะไรที่ขัดขวางผู้ใช้ในการเรียกใช้ put () และส่งผ่านสตริงและออบเจ็กต์

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


2

การสืบทอดสามารถทำลายการห่อหุ้มในบางกรณีและหากเกิดขึ้นมันจะไม่ดี อ่านเพิ่มเติม


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

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

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

ในสาระสำคัญขณะนี้อาจไม่เป็นกรณี แต่

มันง่ายต่อการใช้มรดกเพื่อทำลาย encapsulation

การอนุญาตให้ subclassing อาจผิดถ้าจุดนั้น


1

คำตอบด่วนสั้น ๆ :

มันขึ้นอยู่กับสิ่งที่คุณกำลังทำ

คำตอบที่น่าเบื่อเพิ่มเติม:

ผู้พัฒนาสามารถสร้างแอปได้ ใช้ทั้งคลาสย่อยหรือแม่แบบ (ต้นแบบ) บางครั้งเทคนิคเดียวก็ใช้งานได้ดีกว่า บางครั้ง techinque อื่น ๆ ก็ทำงานได้ดีขึ้น

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

หมายเหตุประกอบ:

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

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