Далі поєднуються деякі найкращі аспекти кількох інших відповідей, а також техніка, яка дозволяє визначити ключовий аспект Cat
наявності Excrement
властивості необхідного RadioactivePoo
типу, але можливості повернути це як просто, Poo
якщо ми лише знаємо, що маємо, AnimalBase
а не зокрема aCat
.
Від абонентів не вимагається використовувати загальні засоби, хоча вони присутні в реалізаціях, а також не викликати інакше названу функцію для отримання спеціальних Poo
.
Проміжний клас AnimalWithSpecialisations
служить лише для герметизації Excrement
властивості, з'єднуючи його через непублічну SpecialPoo
властивість з похідним класом, AnimalWithSpecialPoo<TPoo>
який маєExcrement
властивість похідного типу повернення.
Якщо Cat
є єдиною твариною, Poo
яка будь-якою особливою, або ми не хочемо, щоб тип Excrement
був головною визначальною ознакою a Cat
, проміжний загальний клас може бути пропущений в ієрархії, що Cat
походить безпосередньо з AnimalWithSpecialisations
, але якщо є - це кілька різних тварин, основною характеристикою яких є те, що вони Poo
є якимось особливим чином, поділ "шаблону" на проміжні класи допомагає зберегтиCat
клас досить чистим, хоча і ціною пари додаткових викликів віртуальних функцій.
Приклад коду показує, що більшість очікуваних операцій працюють "як очікувалося".
public interface IExcretePoo<out TPoo>
where TPoo : Poo
{
TPoo Excrement { get; }
}
public class Poo
{ }
public class RadioactivePoo : Poo
{ }
public class AnimalBase : IExcretePoo<Poo>
{
public virtual Poo Excrement { get { return new Poo(); } }
}
public class Dog : AnimalBase
{
}
public abstract class AnimalWithSpecialisations : AnimalBase
{
public sealed override Poo Excrement { get { return SpecialPoo; } }
protected virtual Poo SpecialPoo { get { return base.Excrement; } }
}
public abstract class AnimalWithSpecialPoo<TPoo> : AnimalWithSpecialisations, IExcretePoo<TPoo>
where TPoo : Poo
{
sealed protected override Poo SpecialPoo { get { return Excrement; } }
public new abstract TPoo Excrement { get; }
}
public class Cat : AnimalWithSpecialPoo<RadioactivePoo>
{
public override RadioactivePoo Excrement { get { return new RadioactivePoo(); } }
}
class Program
{
static void Main(string[] args)
{
Dog dog = new Dog();
Poo dogPoo = dog.Excrement;
Cat cat = new Cat();
RadioactivePoo catPoo = cat.Excrement;
AnimalBase animal = cat;
Poo animalPoo = catPoo;
animalPoo = animal.Excrement;
AnimalWithSpecialPoo<RadioactivePoo> radioactivePooingAnimal = cat;
RadioactivePoo radioactivePoo = radioactivePooingAnimal.Excrement;
IExcretePoo<Poo> pooExcreter = cat;
IExcretePoo<RadioactivePoo> radioactivePooExcreter = cat;
animal = dog;
animalPoo = dogPoo;
pooExcreter = dog;
}