ฉันควรจะชอบองค์ประกอบหรือการสืบทอดในสถานการณ์นี้หรือไม่


11

พิจารณาอินเทอร์เฟซ:

interface IWaveGenerator
{
    SoundWave GenerateWave(double frequency, double lengthInSeconds);
}

อินเทอร์เฟซนี้ถูกใช้งานโดยคลาสที่สร้างคลื่นของรูปร่างที่แตกต่างกัน (ตัวอย่างเช่นSineWaveGeneratorและSquareWaveGenerator)

ฉันต้องการใช้คลาสที่สร้างSoundWaveจากข้อมูลดนตรีไม่ใช่ข้อมูลเสียงดิบ มันจะได้รับชื่อของโน้ตและระยะเวลาในแง่ของการเต้น (ไม่กี่วินาที) ที่และภายในใช้IWaveGeneratorฟังก์ชั่นในการสร้างSoundWaveตาม

คำถามคือควรNoteGeneratorประกอบด้วยIWaveGeneratorหรือควรได้รับมรดกจากการIWaveGeneratorดำเนินการ?

ฉันเอนตัวไปยังองค์ประกอบเนื่องจากเหตุผลสองประการ:

1- มันช่วยให้ฉันสามารถฉีดใด ๆIWaveGeneratorไปยังNoteGeneratorแบบไดนามิก นอกจากนี้ฉันต้องการเพียงหนึ่งNoteGeneratorชั้นแทนSineNoteGenerator, SquareNoteGeneratorฯลฯ

2- มีความต้องการไม่ได้ที่จะเปิดเผยอินเตอร์เฟซในระดับต่ำกว่าที่กำหนดโดยNoteGeneratorIWaveGenerator

อย่างไรก็ตามฉันโพสต์คำถามนี้เพื่อฟังความคิดเห็นอื่น ๆ เกี่ยวกับเรื่องนี้อาจเป็นจุดที่ฉันไม่ได้คิด

BTW: ฉันจะบอกว่าNoteGenerator เป็นแนวคิดIWaveGeneratorเพราะมันสร้างSoundWaves

คำตอบ:


14

อนุญาตให้ฉันฉีด IWaveGenerator ใด ๆ ไปยัง NoteGenerator แบบไดนามิก นอกจากนี้ฉันต้องการเพียงหนึ่งคลาส NoteGenerator แทนSineNoteGenerator , SquareNoteGeneratorฯลฯ

นั่นเป็นสัญญาณที่ชัดเจนว่ามันจะเป็นการดีกว่าถ้าจะใช้การเรียบเรียงที่นี่และไม่สืบทอดมาจากSineGeneratorหรือSquareGeneratorหรือ (แย่กว่า) ทั้งคู่ ในขณะที่มันจะทำให้รู้สึกถึงการสืบทอด NoteGenerator โดยตรงจากIWaveGeneratorถ้าคุณเปลี่ยนหลังเล็กน้อย

ปัญหาจริงที่นี่คือมันอาจจะเป็นสิ่งที่มีความหมายที่จะมีNoteGeneratorด้วยวิธีการเช่น

SoundWave GenerateWave(string noteName, double noOfBeats, IWaveGenerator waveGenerator);

แต่ไม่ใช่ด้วยวิธีการ

SoundWave GenerateWave(double frequency, double lengthInSeconds);

เนื่องจากอินเทอร์เฟซนี้เฉพาะเจาะจงเกินไป คุณต้องการIWaveGeneratorที่จะเป็นวัตถุที่สร้างSoundWaves แต่ขณะนี้อินเตอร์เฟซของคุณแสดงออกIWaveGenerators เป็นวัตถุที่สร้างSoundWavesจากความถี่และระยะเวลาเฉพาะ ดังนั้นการออกแบบอินเตอร์เฟซที่ดีขึ้นเช่นนี้

interface IWaveGenerator
{
    SoundWave GenerateWave();
}

และส่งผ่านพารามิเตอร์เช่นfrequencyหรือlengthInSecondsหรือชุดพารามิเตอร์ที่แตกต่างกันอย่างสมบูรณ์ผ่านคอนสตรัคเตอร์ของ a SineWaveGenerator, a SquareGeneratorหรือเครื่องมือสร้างอื่น ๆ ที่คุณมีอยู่ในใจ สิ่งนี้จะช่วยให้คุณสร้างประเภทอื่นIWaveGeneratorที่มีพารามิเตอร์การก่อสร้างที่แตกต่างกันโดยสิ้นเชิง บางทีคุณอาจต้องการเพิ่มเครื่องกำเนิดคลื่นสี่เหลี่ยมซึ่งต้องการความถี่และพารามิเตอร์ความยาวสองตัวหรืออะไรทำนองนั้นบางทีคุณอาจต้องการเพิ่มเครื่องกำเนิดคลื่นสามเหลี่ยมถัดไปด้วยพารามิเตอร์อย่างน้อยสามตัว หรือที่NoteGeneratorมีพารามิเตอร์คอนสตรัคnoteName, และnoOfBeatswaveGenerator

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


ที่น่าสนใจไม่ได้คิดเรื่องนี้ แต่ฉันสงสัยว่า: สิ่งนี้ (การตั้งค่า 'พารามิเตอร์เป็นฟังก์ชัน polymorphic' ในตัวสร้าง) มักใช้งานได้จริงหรือไม่? เพราะจากนั้นรหัสจะต้องรู้ว่ามันเกี่ยวข้องกับประเภทใดจึงทำลายความหลากหลาย คุณช่วยยกตัวอย่างตำแหน่งที่ใช้งานได้ไหม
Aviv Cohn

2
@AvivCohn: "รหัสจะต้องรู้ว่ามันเกี่ยวข้องกับประเภท" - ไม่นั่นคือความเข้าใจผิด เพียงส่วนหนึ่งของรหัสที่สร้างเครื่องกำเนิดชนิดเฉพาะ (mybe a factory) และต้องรู้เสมอว่าเป็นประเภทใด
Doc Brown

... และถ้าคุณต้องการสร้างกระบวนการของวัตถุที่หลากหลายคุณสามารถใช้รูปแบบ "โรงงานนามธรรม" ( en.wikipedia.org/wiki/Abstract_factory_pattern )
Doc Brown

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

9

NoteGenerator นั้นเป็น "แนวคิด" หรือไม่ IWaveGenerator นั้นไม่สำคัญ

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

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


ที่จริงฉันไม่ได้ตั้งใจNoteGeneratorจะใช้GenerateWaveแต่แปลความหมายพารามิเตอร์ต่างกันใช่ฉันยอมรับว่าจะเป็นความคิดที่แย่มาก ฉันหมายถึง NoteGenerator นั้นเป็นความเชี่ยวชาญเฉพาะของเครื่องกำเนิดคลื่น: สามารถรับข้อมูลอินพุต 'ระดับที่สูงขึ้น' แทนข้อมูลเสียงดิบ (เช่นชื่อโน้ตแทนที่จะเป็นความถี่) sineWaveGenerator.generate(440) == noteGenerator.generate("a4")กล่าวคือ ดังนั้นจึงมีคำถามองค์ประกอบหรือการสืบทอดมา
Aviv Cohn

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

@Ixrec: จริง ๆ แล้วมันไม่ยากมากที่จะมีอินเทอร์เฟซเดียวสำหรับเครื่องกำเนิดไฟฟ้าทุกประเภท OP น่าจะทำทั้งสองอย่างใช้องค์ประกอบเพื่อฉีดเครื่องกำเนิดระดับต่ำและสืบทอดจากอินเตอร์เฟสที่เรียบง่าย (แต่ไม่สืบทอด NoteGenerator จาก a การนำเครื่องมือสร้างระดับต่ำมาใช้) ดูคำตอบของฉัน
Doc Brown

5

2- ไม่จำเป็นต้องใช้ NoteGenerator เพื่อแสดงอินเตอร์เฟสระดับล่างที่กำหนดโดย IWaveGenerator

เสียงเหมือนNoteGeneratorไม่ได้WaveGeneratorดังนั้นจึงไม่ควรใช้อินเตอร์เฟซ

องค์ประกอบเป็นตัวเลือกที่ถูกต้อง


ฉันจะบอกว่าNoteGenerator เป็นแนวคิดIWaveGeneratorเพราะมันสร้างSoundWaves
Aviv Cohn

1
ดีถ้ามันไม่จำเป็นที่จะเปิดเผยแล้วมันไม่ได้เป็นGenerateWave IWaveGeneratorแต่ดูเหมือนว่าจะใช้ IWaveGenerator (อาจจะมากกว่านั้น) องค์ประกอบจึง
Eric King

@EricKing: นี่เป็นคำตอบที่ถูกต้องตราบใดที่ยังต้องติดกับGenerateWaveฟังก์ชั่นตามที่เขียนไว้ในคำถาม แต่จากความคิดเห็นข้างต้นฉันคิดว่านั่นไม่ใช่สิ่งที่ OP คิดไว้ในใจ
Doc Brown

3

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


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

2

มันจะดีสำหรับNoteGeneratorการใช้อินเตอร์เฟซและยังสำหรับการNoteGeneratorที่จะมีการดำเนินการภายในที่อ้างอิง (โดยองค์ประกอบ) IWaveGeneratorอีก

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

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


1
ไม่NoteGeneratorสามารถนำไปใช้ได้IWaveGeneratorเนื่องจากเนื่องจากโน้ตต้องการจังหวะ ไม่วินาที -
Tulains Córdova

ใช่แน่นอนถ้าไม่มีการใช้อินเตอร์เฟสที่สมเหตุสมผลแล้วคลาสไม่ควรใช้งาน อย่างไรก็ตาม OP ได้ระบุว่า "ฉันจะบอกว่าNoteGeneratorเป็นแนวคิดIWaveGeneratorเพราะมันสร้างSoundWaves" และเขากำลังพิจารณาการสืบทอดดังนั้นฉันจึงใช้ละติจูดทางจิตสำหรับความเป็นไปได้ที่อาจมีการใช้อินเทอร์เฟซแม้ว่าจะมีอีก อินเตอร์เฟซที่ดีขึ้นหรือลายเซ็นสำหรับชั้นเรียน
Erik Eidt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.