Kalıtım yanlış gitti


12

İyi bir miras modeli yokuş aşağı gitti bazı kod var ve neden ve nasıl düzeltmek için anlamaya çalışıyorum. Temel olarak, bir Zoo hiyerarşisine sahip olduğunuzu düşünün:

class Animal  
class Parrot : Animal 
class Elephant : Animal 
class Cow : Animal

vb.

Sizin eat (), run (), vb yöntemleri var ve hepsi iyi. Sonra bir gün birisi gelir ve der ki - CageBuilder sınıfımız harika çalışıyor ve çok güçlü ve duvarı parçalayabilen yeni Afrika Bisonu hariç animal.weight () ve animal.height () kullanıyor, bu yüzden ekleyeceğim Animal sınıfına bir özellik daha - isAfricanBizon () ve bunu malzeme seçerken kullanın ve sadece AfricanBizon sınıfı için geçersiz kılın. Sonraki kişi gelir ve benzer bir şey yapar ve bildiğiniz bir sonraki şey, temel sınıfa hiyerarşinin bazı alt kümelerine özgü tüm bu özelliklere sahip olduğunuzu bilir.

Bu kodu geliştirmenin / yeniden düzenlemenin iyi bir yolu nedir? Buradaki alternatiflerden biri, türleri kontrol etmek için dynamic_casts kullanmaktır, ancak arayanları kümelendirir ve bir sürü if-then-else ekler. Burada daha spesifik arayüzlere sahip olabilirsiniz, ancak sahip olduğunuz tek şey çok da yardımcı olmayan temel sınıf referansıysa. Başka öneriniz var mı? Örnekler?

Teşekkürler!


@ James: o zaman elle ayrıştırıcılar yazmanız gerekir. : S
Matteo Italia

5
Açıkçası bu çok saçma bir müşteri gereksinimi örneğidir. Afrika'da bizon yok. Gerçekle bağlantısı olmayan nesne modelleri tasarlayamazsınız. Bu gerçeklik dolar dolu eller tarafından yaratılmadıkça. Hangi sorunu çözer.
Hans Passant

1
Tüm bizonu yer misin? [Bunu daha önce yayınladım ama bir nedenden ötürü, muhtemelen
mizahsız

CageBuilder'ın kendi sınıfına ihtiyacı var mı? Her bir sınıf tarafından geçersiz kılınabilen varsayılan bir MakeCage yöntemi varsa ne olur.
Meslek

1
Arayanlar için bir dezavantaj olarak if-then-else dağınıklığından bahsedersiniz, ancak arayanlar isAfricanBizon () kullanmaya başlar başlamaz kodu otomatik olarak if-then-else ile karıştırırlar. Yani ya isAfricanBizon () ile if-then-else dağınıklığı ya da dinamik dökümlerle if-then-else dağınıklığı.
davidk01

Yanıtlar:


13

Sorun, AlsoConcreteWall () uygulamak yerine, IsAfricanBison () bayrağı çağrısı uyguladı ve duvarın sınıf kapsamı dışında değişip değişmeyeceğine dair mantığı taşıdı. Sınıflarınız kimlik değil davranış ve gereksinimleri ortaya çıkarmalıdır; bu sınıflardaki tüketicileriniz, ne olduklarına göre değil, söylendikleriyle çalışmalıdır.


1
-1: Sadece ne yapılmayacağını söylüyor . OP zaten bunun kötü bir fikir olduğunu biliyor, dolayısıyla soru.
Steven Evers

12

isAfricanBizon () genel değildir. Hayvan çiftliğinizi de çok güçlü olan ancak doğru etkiyi elde etmek için isAfricanBizon () 'dan doğru dönen bir hipopotam ile genişlettiğinizi varsayalım.

Arayüze her zaman belirli soruya cevap veren yöntemler eklemek istersiniz, bu durumda kuvvet () gibi bir şey olurdu


+1: Diğer herkes, bu özel kullanım durumunu karşılamak için sınıfın kavramsal modelini kırıyor gibi görünüyor (sadece farklı hayvan türlerinin özelliklerini kapsülliyor). Bir strengthyöntem material.canHold(animal), farklı malzeme türlerini desteklemenin temiz bir yoluna izin verilerek sorgulanabilir ConcreteWall.
Aidan Cully

Ben gelecekteki gereksinimleri etkinleştirmek için daha esnek olduğundan, kuvvet () özelliği yaklaşım Diğerlerinin RequectConcreteWall () önerisinden daha iyi gibi. Yeni başlayanlar için, CageBuilder sınıfını hangi malzemelerin yeterince güçlü olduğuna karar verin ve ardından sınıfı yeni malzemelerle kolayca genişletebilirsiniz.
jhocking

3

Ben senin sorunun bu olduğunu düşünüyorum: kütüphanenin sadece hiyerarşinin bir alt kümesiyle ilgilenen, ancak temel sınıfa bir işaretçi / başvuru geçirilen çeşitli istemcileri var. Aslında dynamic_cast <> sorununu çözmek için orada.

Dynamic_cast kullanımını en aza indirmek istemcilerin tasarımıyla ilgilidir <>; nesnenin özel muamele gerektirip gerektirmediğini belirlemek için kullanmalı ve eğer öyleyse aşağı dökülen referanstaki tüm işlemleri yapmalıdırlar.

Birkaç ayrı alt hiyerarşiye uygulanan "karma" tipte işlevsellik koleksiyonunuz varsa, Java ve C # 'ın kullandığı arabirim desenini kullanmak isteyebilirsiniz; salt sanal sınıf olan bir sanal temel sınıfınız varsa ve bir örneğin kendisi için bir uygulama sağlayıp sağlamadığını belirlemek için dynamic_cast <> kullanın.


1

Yapabileceğiniz bir şey, isAfricanBison()aslında ilgilendiğiniz özelliklerin kontrolüyle, örneğin türün açık kontrolünü değiştirmektir isTooStrong().


1
ne için isTooStrong ()? Hayvan sınıfına kafese özel kod ekliyorsunuz.
Steven Evers

1

Hayvanlar beton duvarları önemsememelidir. Belki bunu basit değerlerle ifade edebilirsiniz.

class Animal {
public:
  virtual ~Animal() {}
  virtual size_t height() const = 0;
  virtual size_t weight() const = 0;
  virtual bool isStrong() const = 0;
};

Cage *CreateCageFromSQL(Animal &a);
Cage *CreateCageFromOrangePeelsAndSticks(Animal &a);

Bunun mümkün olmadığını sanıyorum. Yine de oyuncak örnekleriyle ilgili sorun budur.

Ben asla RequiConcreteWalls () veya çizgiler ve dinamik işaretçi dökümleri herhangi bir oranda görmek istemem.

Bu genellikle ucuz bir çözümdür. Bakımı ve kavramsallaştırması kolaydır. Ve gerçekten, sorun zaten hayvan türüne bağlı olduğunu belirtiyor.

class Animal {
public:
  virtual ~Animal() {}
  virtual CageBuilder *getCageBuilder() = 0;
};

Bu da paylaşılan kodu kullanmanıza engel olmaz, sadece Animal'i biraz kirletir.

Ancak kafesin nasıl kurulduğu başka bir sistemin politikası olabilir ve belki de hayvan başına birden fazla tipte kafes oluşturucunuz olabilir. Gelebileceğiniz birçok garip ve kıvrımlı kombinasyon var.

Ben kullandım Bileşen Tabanlı Tasarım iyi uçlarına, onunla asıl sorun Hayvanların mülkiyet paylaşıldığı zaman zahmetli olabilir olmasıdır. Acı noktası olan yıkıcılara atmaktan nasıl kaçınılır.

Double Dispatch başka bir seçenektir, ancak her zaman içine atlamak için sabırsızlanıyordum.

Bunun ötesinde problemi tahmin etmek zor.


0

Elbette tüm Hayvanlar doğal bir özelliğe sahiptir attemptEscape(). Bazı yöntem falsetüm senaryolarda bir sonuç verebilirken, diğerleri sizeve gibi diğer içsel özelliklerinin sezgisel özelliklerine dayalı bir şansa sahip olabilir weight. Sonra kesinlikle bir noktada attemptEscape()kesinlikle geri döner gibi önemsiz hale gelir true.

Korkarım sorunuzu tam olarak anlayamıyorum ... tüm hayvanların ilgili eylemleri ve özellikleri var. Hayvana özgü olanlar uyduğu yere sokulmalıdır. Bison'u doğrudan Papağanlar ile ilişkilendirmeye çalışmak iyi bir miras kurulumu değildir ve gerçekten uygun bir tasarımda bir sorun olmamalıdır.


-1

Başka bir seçenek, her hayvan için uygun kafesler oluşturan bir fabrika kullanmak olacaktır. Her biri için koşulların çok farklı olması durumunda bunun daha iyi olabileceğini düşünüyorum. Ancak bu sadece bir koşulsa, yukarıda belirtilen RequiresConcreteWall()yöntem bunu yapacaktır.


-1

RecommendedConcreteWall () yerine tam olarak AllowendCageType ()


-2

Neden böyle bir şey yapmıyorsun

class Animals { /***/ } class HeavyAnimals{} : Animals //The basic class for animals like the African Bison

HeavyAnimals sınıfıyla HeavyAnimals sınıfını genişleterek African Bison sınıfını oluşturabilirsiniz.

Yani artık HeavyAnimal sınıfı gibi başka bir temel sınıf oluşturmak için kullanılabilecek ana sınıf (Hayvanlar), Afrika Bison sınıfını ve diğer Ağır Hayvanları oluşturmak için kullanılabilir. Bu yüzden African Bison ile artık Animal sınıfı yöntemlerine ve mülküne (bu, tüm hayvanlar için bir temel) ve HeavyAnimals sınıfına (bu, Ağır Hayvanlar için temel) erişiminiz var.


2
Bu bir karma veya özellik olarak işe yarayabilir, ancak kesinlikle bir alt sınıf olarak kullanılamaz. Bu, başka bir özelliğe ihtiyaç duyulduğunda çoklu kalıtım için yalvarıyor.
Sıradan
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.