Bu senaryoda kompozisyon veya miras tercih etmeli miyim?


11

Bir arayüz düşünün:

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

Bu arayüz, farklı şekillerdeki dalgaları (örneğin SineWaveGeneratorve SquareWaveGenerator) üreten birkaç sınıf tarafından uygulanır .

SoundWaveHam ses verilerine değil, müzik verilerine dayanan bir sınıf uygulamak istiyorum . Bir notun adını ve vuruş sayısı (saniye değil) cinsinden bir uzunluğunu alır ve buna göre IWaveGeneratoroluşturmak için dahili olarak işlevselliği kullanır SoundWave.

Soru şudur, NoteGeneratoriçerme IWaveGeneratorbir IWaveGeneratoruygulamadan mı yoksa bir uygulamadan miras almalı mı?

İki nedenden dolayı kompozisyona yöneliyorum:

1- Bu beni herhangi enjekte sağlar IWaveGeneratorüzere NoteGeneratordinamik. Ayrıca, sadece bir ihtiyaç NoteGeneratoryerine sınıfını, SineNoteGenerator, SquareNoteGeneratorvb

2- NoteGeneratortarafından tanımlanan alt seviye arayüzü ortaya çıkarmaya gerek yoktur IWaveGenerator.

Ancak bu soruyu, belki de hiç düşünmediğim diğer fikirleri duymak için gönderiyorum.

BTW: söyleyebilirim NoteGenerator olan bir kavramsal IWaveGeneratoroluşturduğu çünkü SoundWaves.

Yanıtlar:


14

Herhangi bir IWaveGenerator'ı NoteGenerator'a dinamik olarak enjekte etmeme izin veriyor. Ayrıca, SineNoteGenerator , SquareNoteGenerator vb . Yerine yalnızca bir NoteGenerator sınıfına ihtiyacım var .

Bu, burada kompozisyon kullanmanın daha iyi olacağını ve her ikisinden SineGeneratormi SquareGeneratoryoksa (veya daha kötüsünü) miras almamanın açık bir işaretidir . Bununla birlikte, IWaveGeneratorikincisini biraz değiştirirseniz, doğrudan bir NoteGenerator'ı miras almak mantıklı olacaktır .

Asıl sorun burada sahip olmak muhtemelen anlamlı olduğu NoteGeneratorbir yöntem gibi olan

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

ama bir yöntemle değil

SoundWave GenerateWave(double frequency, double lengthInSeconds);

çünkü bu arayüz çok özel. IWaveGeneratorS nin s üreten nesneler olmasını istiyorsunuz SoundWave, fakat şu anda arabiriminiz s'nin yalnızca frekans ve uzunluktan s IWaveGeneratorüreten nesneler olduğunuSoundWave ifade ediyor . Böyle bir arayüzü bu şekilde daha iyi tasarlayın

interface IWaveGenerator
{
    SoundWave GenerateWave();
}

ve frequencyveya lengthInSeconds, veya tamamen farklı bir parametre kümesini a SineWaveGenerator, a SquareGeneratorveya aklınızdaki diğer jeneratörlerin yapıcılarından geçirin. Bu, IWaveGeneratortamamen farklı inşaat parametrelerine sahip başka türler oluşturmanıza izin verecektir . Belki bir frekansa ve iki uzunluk parametresine ihtiyaç duyan bir dikdörtgen dalga jeneratörü eklemek istersiniz, ya da bunun gibi bir şey, belki de en az üç parametre ile bir üçgen dalga jeneratörü eklemek istersiniz. Ya da, bir NoteGenerator, yapıcı parametreleri ile noteName, noOfBeatsve waveGenerator.

Buradaki genel çözüm, giriş parametrelerini çıkış işlevinden ayırmak ve yalnızca çıkış işlevini arabirimin bir parçası yapmaktır.


İlginç, bunu düşünmedim. Ama merak ediyorum: Bu (yapıcıda 'parametreleri bir polimorfik fonksiyona ayarlamak') genellikle gerçekte çalışıyor mu? Çünkü o zaman kod gerçekten hangi türle uğraştığını bilmek zorunda kalacak, böylece Polimorfizmi mahvedecektir. Bunun işe yarayacağı bir örnek verebilir misiniz?
Aviv Cohn

2
@AvivCohn: "kod gerçekten ne tür bir şeyle uğraştığını bilmek zorunda kalacak" - hayır, bu bir yanlış anlama. Kodun sadece belirli bir jeneratör türünü (mybe bir fabrika) oluşturan ve her zaman hangi türle uğraştığını bilmesi gerekir.
Doc Brown

... ve nesnelerinizin yapım sürecini polimorfik hale getirmeniz gerekiyorsa, "soyut fabrika" desenini kullanabilirsiniz ( en.wikipedia.org/wiki/Abstract_factory_pattern )
Doc Brown

Bu benim seçeceğim çözüm. Küçük, değişmez sınıflar buraya gitmek için doğru yoldur.
Stephen

9

NoteGenerator'ün "kavramsal olarak" olup olmadığı bir IWaveGenerator önemli değildir.

Bir arabirimden yalnızca bu kesin arabirimi Liskov İkame İlkesine göre, yani doğru anlambilim ve doğru sözdizimi ile uygulamayı planlıyorsanız miras almalısınız.

NoteGenerator'ınızın sözdizimsel olarak aynı arayüze sahip olabileceği anlaşılıyor, ancak semantiği (bu durumda, aldığı parametrelerin anlamları) çok farklı olacak, bu nedenle mirasın kullanılması oldukça yanıltıcı ve potansiyel olarak hataya açık olacaktır. Burada kompozisyonu tercih etme hakkınız var.


Aslında parametreleri farklı şekilde NoteGeneratoruygulayacağımı kastetmedim GenerateWave, evet bunun korkunç bir fikir olacağını kabul ediyorum. NoteGenerator, bir dalga jeneratörü uzmanlığı anlamına geliyordu: sadece ham ses verileri yerine 'daha yüksek seviye' giriş verilerini alabilir (örneğin, frekans yerine bir not adı). Yani sineWaveGenerator.generate(440) == noteGenerator.generate("a4"). Soru, kompozisyon ya da miras geliyor.
Aviv Cohn

Hem yüksek hem de düşük seviye dalga üretme sınıflarına uyan tek bir arayüzle gelebilirseniz, kalıtım kabul edilebilir. Ancak bu çok zor görünüyor ve gerçek faydaları olma ihtimali düşük Kompozisyon kesinlikle daha doğal bir seçim gibi görünüyor.
Ixrec

@Ixrec: aslında, tüm jeneratör tipleri için tek bir arayüze sahip olmak çok zor değil, OP muhtemelen her ikisini de yapmalı, düşük seviyeli bir jeneratörü enjekte etmek ve basitleştirilmiş bir arayüzden miras almak için kompozisyon kullanmalıdır (ancak NoteGenerator'ı bir düşük seviyeli jeneratör uygulaması) Cevabımı görün.
Doc Brown

5

2- NoteWenerator'un IWaveGenerator tarafından tanımlanan daha alt seviye arayüzü göstermesine gerek yoktur.

Kulağa ses gibi NoteGeneratordeğil WaveGenerator, bu nedenle arayüzü uygulamamalıdır.

Kompozisyon doğru seçimdir.


Söyleyebilirim NoteGenerator is bir kavramsal IWaveGeneratoroluşturduğu çünkü SoundWaves.
Aviv Cohn

1
Eğer açığa çıkarmaya gerek yoksa GenerateWave, o zaman bir değil IWaveGenerator. Ama bir IWaveGenerator (belki daha fazla?), Yani kompozisyon kullanıyor gibi görünüyor .
Eric King

@EricKing: soruya GenerateWaveyazıldığı gibi işleve bağlı kalmak zorunda kaldığı sürece bu doğru bir cevaptır . Ama yukarıdaki yorumdan, sanırım OP'nin gerçekten aklında olan şey bu değildi.
Doc Brown

3

Kompozisyon için sağlam bir vakanız var. Sen bir durum var olabilir ayrıca devralma ekleyin. Anlatmanın yolu arama koduna bakmaktır. NoteGeneratorMevcut bir arama kodunu bekleyen bir arama kodunu kullanmak IWaveGeneratoristiyorsanız, arabirimi uygulamanız gerekir. Değiştirilebilirliğe ihtiyacınız var. Kavramsal olarak "is-a" dalga üreteci olup olmadığı noktanın yanındadır.


Bu durumda, örneğin kompozisyonu seçmek, ancak yine de ikame edilebilirliğin gerçekleşmesi için bu mirasa ihtiyaç duyulması halinde, "miras" örneğin adlandırılır IHasWaveGeneratorve bu arabirimdeki ilgili yöntemin GetWaveGeneratorbir örneğini döndürür IWaveGenerator. Tabii ki adlandırma değiştirilebilir. (Sadece daha fazla ayrıntıya girmeye çalışıyorum - bilgilerimin yanlış olup olmadığını bana bildirin.)
rwong

2

NoteGeneratorArayüzü uygulamak ve ayrıca NoteGenerator(kompozisyona göre) bir başkasına referans veren dahili bir uygulamaya sahip olmak iyidir IWaveGenerator.

Genel olarak, kompozisyon daha sürdürülebilir (yani okunabilir) bir kodla sonuçlanır, çünkü üzerinde düşünülecek geçersiz kılma karmaşıklıkları yoktur. Kalıtım kullanırken sahip olacağınız sınıf matrisi hakkındaki gözleminiz de önemlidir ve muhtemelen kompozisyona işaret eden bir kod kokusu olarak düşünülebilir.

Kalıtım, özelleştirmek veya özelleştirmek istediğiniz bir uygulama olduğunda, burada durum böyle görünmüyorsa daha iyi kullanılır: sadece arayüzü kullanmanız gerekir.


1
O Tamam değil NoteGeneratoruygulamak için IWaveGeneratornotlar atım gerektirdiğinden dolayı. saniye değil.
Tulains Córdova

Evet, kesinlikle arayüzün mantıklı bir uygulaması yoksa, sınıf onu uygulamamalıdır. Bununla birlikte, OP " NoteGeneratorkavramsal olarak bir s IWaveGeneratorolduğunu çünkü bir şey ürettiğini SoundWave" söyledi ve miras düşünüyor, bu yüzden başka bir şey olsa bile, arayüzün bazı uygulamaları olabilir olasılığı için zihinsel enlem aldı. sınıf için daha iyi arayüz veya imza.
Erik Eidt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.