我正在使用一个小型的层次结构,类似于以下内容,并且可以说在我悲伤的野生动物园世界中不会有任何其他动物类型(我完全不担心扩展的弹性):
public abstract class Animal {};
public sealed class Dog : Animal {};
public sealed class Cat : Animal {};
public sealed class Turtle : Animal {};
public sealed class Bird : Animal {};
就API而言,我想对所有动物进行类似处理,但显然它们在以下情况下的反应略有不同:
public class AnimalCare {
public void Feed<T> (T pet) where T: Animal;
public T PickUp<T> (PetStore store);
}
乍一看,在没有在Animal类上放置feed方法的情况下执行feeding的想法(对不起,我知道这个例子正在达到这一点,但是为了让争论起来,假装AnimalCare是我的Animal模型的视图并且我非常坚持关注的分离)会建议访客。但我真正想做的是让上面是AnimalCare唯一需要担心的API消费者,而我做的事情如下:
public class AnimalCare {
public void Feed<T> (T pet) where T : Animal;
public T PickUp<T> (PetStore store);
public void Feed<Dog> (Dog rover)
{
// dump out alpo, lift toilet seat
}
public void Feed<Turtle> (Turtle franklin)
{
// do turtles eat? let him figure it out himself.
} // ...etc.
public Dog PickUp<Dog> (PetStore store)
{
// get bone, tennis ball, leash
}
public Bird PickUp<Bird> (PetStore store)
{
// make sure not dead, nailed to perch
} // ...etc.
}
等等。我知道第一部分(Feed()方法)可以在不需要泛型的情况下重载,但是这仍然让我对PickUp实施了一个尴尬的实现(因为我上面已经草拟了它不合法实现),像PickUpDog // PickUpBird等一样悲惨。我非常希望避免为AnimalCare的消费者及其志同道合的朋友提供单独的“观点”。
我一直在玩嵌套专门课程和其他奇怪的构图或界面重构尝试,但我似乎无法做到正确而且我被卡住了。是否有一种干净的方式来做我想要的事情,或者我是否为每个具体动物实施AnimalCare而辞职?
修改
Joel关于工厂/存储库的观点让我想到了更多。让我们把感兴趣的方法称为更合理:
public class AnimalPresentation {
public void Show (Dog dog);
public void Show (Cat cat);
//..etc.
public Animal Get (PetStore store);
}
我认为这首先会更有意义。从GetStore中获取的Animal类型在调用Get时是未知的,但在Get的一般实现中,一旦确定了类型,它就会分支到特定的重载。 PetStore的特定子类型是最佳/唯一的前进方式吗?
答案 0 :(得分:1)
修改强>
你可能会考虑这样的事情:
首先,你的动物就有一个界面IAnimal
。他们不知道任何方法。然后创建一个新的界面,如
interface IAnimalCareClient<T>
{
void Init(T animal);
void Feed();
T Pickup(PetStore store);
}
然后为每种动物类型创建客户端,如
class DogClient : IAnimalCareClient<Dog>
{
void Init(Dog animal) { //bla }
void Feed() {}
Dog Pickup(PetStore store) { //bla again }
}
然后您的客户应该存储在某个列表中,并且类型可以存在于各种dll等中。您只需要获取引用。
然后只是
class AnimalCare
{
void Feed<T>(T animal)
{
//GrabWorkerFromStack<T>() should return the type IAnimalCareClient<T>
IAnimalCareClient<T> worker = Activator.CreateInstance(GrabWorkerFromStack<T>());
worker.Init(animal);
worker.Feed();
}
}
原始回答
基本上你想要对动物本身负责,因为你不能强迫你的AnimalCare
类来实现所有动物的所有属性。然后变成简单的东西:
interface IAnimal
{
void Feed();
IAnimal Pickup(PetStore store);
}
class Dog : IAnimal
{
void Feed() { /* feed */ }
IAnimal Pickup(PetStore store) { /* grab animal and return */ }
}
class AnimalCare
{
void Feed(IAnimal animal)
{
animal.Feed();
}
T Pickup<T>(T animal, PetStore store) where T: IAnimal
{
return (T)animal.Pickup(store);
}
}
答案 1 :(得分:1)
我认为AnimalCare
的问题在于它本质上是Animal
个实例的工厂,或者至少访问Animal
存储库。因此,您的PickUp
函数可能会返回Animal
对象而不是特定类型。
或者,您是否可以将AnimalCare
基于特定类型(AnimalCare<T>
)?这个例子在实际意图方面很难衡量,如果这些想法似乎没有达到标准,那么道歉。
答案 2 :(得分:0)
你可以使用接口,我不知道具体的要求是什么,但如果动物可能有一个可能与其他动物共享的特性,但没有接口是一个良好的开端。
例如:
interface IFeedable
{
void Feed();
}
interface IMammal<TChild>
where TChild: IMammal<TChild>
{
IEnumerable<TChild> Reproduce();
}
class Dog: IFeedable, IMammal<Puppy>
{
// ...
}
我一直使用接口(可能过度使用接口),特别是在这种情况下,正常继承可能不是最佳方式。