बहुत सारे मामलों में मेरे पास कुछ व्यवहार के साथ एक मौजूदा वर्ग हो सकता है:
class Lion
{
public void Eat(Herbivore herbivore) { ... }
}
... और मेरे पास एक इकाई परीक्षण है ...
[TestMethod]
public void Lion_can_eat_herbivore()
{
var herbivore = buildHerbivoreForEating();
var test = BuildLionForTest();
test.Eat(herbivore);
Assert.IsEaten(herbivore);
}
अब, क्या होता है मुझे शेर के पहचान वाले व्यवहार के साथ एक टाइगर वर्ग बनाने की आवश्यकता है:
class Tiger
{
public void Eat(Herbivore herbivore) { ... }
}
... और चूँकि मुझे वही व्यवहार चाहिए, मुझे उसी परीक्षा को चलाने की ज़रूरत है, मैं कुछ इस तरह से करता हूँ:
interface IHerbivoreEater
{
void Eat(Herbivore herbivore);
}
... और मैंने अपना परीक्षण फिर से कराया:
[TestMethod]
public void Lion_can_eat_herbivore()
{
IHerbivoreEater_can_eat_herbivore(BuildLionForTest);
}
public void IHerbivoreEater_can_eat_herbivore(Func<IHerbivoreEater> builder)
{
var herbivore = buildHerbivoreForEating();
var test = builder();
test.Eat(herbivore);
Assert.IsEaten(herbivore);
}
... और फिर मैं अपनी नई Tigerकक्षा के लिए एक और परीक्षा जोड़ता हूं :
[TestMethod]
public void Tiger_can_eat_herbivore()
{
IHerbivoreEater_can_eat_herbivore(BuildTigerForTest);
}
... और फिर मैं अपनी Lionऔर Tigerकक्षाएं फिर से शुरू करता हूं (आमतौर पर विरासत द्वारा, लेकिन कभी-कभी रचना द्वारा):
class Lion : HerbivoreEater { }
class Tiger : HerbivoreEater { }
abstract class HerbivoreEater : IHerbivoreEater
{
public void Eat(Herbivore herbivore) { ... }
}
...और सब ठीक है। हालाँकि, चूंकि कार्यक्षमता अब HerbivoreEaterकक्षा में है, इसलिए अब ऐसा लगता है कि प्रत्येक उपवर्ग पर इन व्यवहारों के लिए परीक्षण होने में कुछ गड़बड़ है। फिर भी यह उपवर्ग हैं जो वास्तव में उपभोग किए जा रहे हैं, और यह केवल एक कार्यान्वयन विवरण है कि वे अतिव्यापी व्यवहार साझा करने के लिए होते हैं ( Lionsऔर Tigersउदाहरण के लिए पूरी तरह से अलग-अलग उपयोग हो सकते हैं)।
यह एक ही कोड को कई बार परीक्षण करने के लिए बेमानी लगता है, लेकिन ऐसे मामले हैं जहां उपवर्ग बेस क्लास की कार्यक्षमता को ओवरराइड और कर सकता है (हाँ, यह एलएसपी का उल्लंघन कर सकता है, लेकिन इसका सामना करना पड़ता है, IHerbivoreEaterयह सिर्फ एक सुविधाजनक परीक्षण इंटरफ़ेस है - यह अंत उपयोगकर्ता के लिए कोई फर्क नहीं पड़ता)। इसलिए इन परीक्षणों का कुछ मूल्य है, मुझे लगता है।
इस स्थिति में अन्य लोग क्या करते हैं? क्या आप अपने परीक्षण को आधार वर्ग में ले जाते हैं, या आप अपेक्षित व्यवहार के लिए सभी उपवर्गों का परीक्षण करते हैं?
संपादित करें :
@Pdr के उत्तर के आधार पर मुझे लगता है कि हमें इस पर विचार करना चाहिए: IHerbivoreEaterयह सिर्फ एक विधि हस्ताक्षर अनुबंध है; यह व्यवहार को निर्दिष्ट नहीं करता है। उदाहरण के लिए:
[TestMethod]
public void Tiger_eats_herbivore_haunches_first()
{
IHerbivoreEater_eats_herbivore_haunches_first(BuildTigerForTest);
}
[TestMethod]
public void Cheetah_eats_herbivore_haunches_first()
{
IHerbivoreEater_eats_herbivore_haunches_first(BuildCheetahForTest);
}
[TestMethod]
public void Lion_eats_herbivore_head_first()
{
IHerbivoreEater_eats_herbivore_head_first(BuildLionForTest);
}
Eatव्यवहार को आधार वर्ग में रखते हैं, तो सभी उपवर्गों को समान Eatव्यवहार प्रदर्शित करना चाहिए । हालाँकि, मैं 2 अपेक्षाकृत असंबंधित वर्गों के बारे में बात कर रहा हूँ जो किसी व्यवहार को साझा करने के लिए होते हैं। उदाहरण के लिए विचार करें,, Flyके व्यवहार Brickऔर Personजो है, हम यह मान सकते हैं, एक ऐसी ही उड़ान व्यवहार प्रदर्शित करते हैं, लेकिन यह जरूरी मतलब नहीं है उन्हें एक आम आधार वर्ग से निकाले जाते हैं है।
Animalवर्ग नहीं होना चाहिए जिसमें शामिल हैEat? सभी जानवर खाते हैं, और इसलिएTigerऔरLionवर्ग जानवर से विरासत में मिल सकता है।