ในหลายกรณีฉันอาจมีคลาสที่มีอยู่แล้วซึ่งมีพฤติกรรมบางอย่าง:
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);
}
ตอนนี้สิ่งที่เกิดขึ้นคือฉันต้องสร้างคลาส Tiger ที่มีพฤติกรรมเฉพาะตัวของ Lion:
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);
}
... และจากนั้นฉัน refactor ของฉันLion
และTiger
ชั้นเรียน (โดยปกติจะเป็นมรดก แต่บางครั้งก็เรียงตาม):
class Lion : HerbivoreEater { }
class Tiger : HerbivoreEater { }
abstract class HerbivoreEater : IHerbivoreEater
{
public void Eat(Herbivore herbivore) { ... }
}
... และทุกอย่างก็ดี อย่างไรก็ตามเนื่องจากฟังก์ชั่นอยู่ในHerbivoreEater
ชั้นเรียนตอนนี้รู้สึกว่ามีบางอย่างผิดปกติในการทดสอบพฤติกรรมเหล่านี้ในแต่ละคลาสย่อย แต่มันเป็นคลาสย่อยที่ถูกใช้งานจริงและเป็นเพียงรายละเอียดการใช้งานที่เกิดขึ้นเพื่อแบ่งปันพฤติกรรมที่ทับซ้อนกัน ( Lions
และTigers
อาจมีการใช้ปลายทางที่แตกต่างกันโดยสิ้นเชิง)
ดูเหมือนว่าซ้ำซ้อนในการทดสอบรหัสเดียวกันหลายครั้ง แต่มีบางกรณีที่คลาสย่อยสามารถและแทนที่ฟังก์ชันการทำงานของคลาสฐาน (ใช่มันอาจละเมิด LSP แต่ให้เผชิญหน้ามัน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
ชั้นสามารถสืบทอดจากสัตว์