我有一个基类.如何为正确的派生类调用正确的方法
本文关键字:派生 调用 方法 有一个 基类 | 更新日期: 2023-09-27 18:14:58
显然是想简化问题。我有一个基类和一些派生类:
public class Mammal { }
public class Cat : Mammal { }
public class Dog : Mammal { }
和一个实用程序类:
public static class AnotherClass
{
public static void GiveFood(Cat cat) {}
public static void GiveFood(Dog dog) {}
}
另一个地方是一个方法,Feed,它接受一个哺乳动物,从那里我想调用正确的重载AnotherClass:
public void Feed(Mammal mammal) {
// if mammal is a cat, call the AnotherClass.GiveFood overload for cat,
// if it's a dog, call the AnotherClass.GiveFood for dog, etc.
}
一种方法是这样做:
public void Feed(Mammal mammal) {
if (mammal is dog)
AnotherClass.GiveFood((Dog)mammal);
if (mammal is Cat)
AnotherClass.GiveFood((Cat)mammal);
}
…但实际上我有很多从哺乳动物衍生出来的动物。有没有更好的方法来做我想在Feed()中做的事情?是否有任何方法可以避免Feed()最终成为一个巨大的丑陋的方法充满了这些"如果x是y然后调用z"的语句?
我通常不喜欢使用dynamic
,但这是我认为合适的情况之一:
public void Feed(Mammal mammal) {
Anotherclass.GiveFood((dynamic)mammal);
}
这将在运行时解决正确的重载,而无需事先知道类型。
严格地说,这可能不是最快的方法,但正如您指出的那样,替代方法维护起来很痛苦,而且/或者很难阅读。在这种情况下,动态分派是优雅的,并将自动合并您将来添加的任何重载。
正如Chris Sinclair指出的那样,你还可以添加一个catch - all方法来检测任何无效调用,并提供一个比没有匹配的GiveFood()
重载时收到的运行时错误更友好的异常:
public static class AnotherClass
{
public static void GiveFood(Cat cat) {}
public static void GiveFood(Dog dog) {}
public static void GiveFood(Mammal mammal)
{
throw new AnimalNotRecognizedException("I don't know how to feed a " + mammal.GetType().Name + ".");
}
}
我认为处理食物是动物的责任,而不是喂食者。否则,您将遇到现在遇到的问题:
public void Feed(Mammal mammal) {
if (mammal is Duck)
{
((Duck)mammal).PryOpenBeak();
((Duck)mammal).InsertFeedingTube();
((Duck)mammal).PourDownFood();
}
}
等等,尽管鸭子不是哺乳动物。
无论如何,你的Mammal
类应该有一个抽象方法Feed(Food food)
,动物自己将不得不弄清楚如何处理食物。这样,当以后添加新的哺乳动物时,您就不必用这个新哺乳动物的喂养逻辑来更新喂食器。
@Chris的评论:然后动物可以实现适当的IFoodXEater
接口,其中包含Feed(IFoodX)
方法,然后喂食器可以查找,尽管这样你就回到了起点:
if (mammal is IFishEater)
{
((IFishEater)mammal).Feed(new Fish());
}
我的建议:
步骤1:创建接口IMammal
<!-- language: c# -->
public interface IMammal
{
void Feed();
}
步骤2:(可选)实现一个基类BaseMammal
public class BaseMammal : IMammal
{
public void Feed()
{
Trace.Write("basic mammal feeding");
//a basic implementation of feeding, common to all or most mammals
}
}
步骤3:实现继承的类
public class Cat : BaseMammal
{
public void Feed()
{
Trace.Write("cat feeding");
BePicky();//some custom cat like functionality
base.Feed(); //and afterwards its still just a mammal after all
}
}
public class Gruffalo : BaseMammal
{
public void Feed()
{
Trace.Write("Gruffalo feeding");
WeirdWayOfEating();//the base implementation is not appropriate
}
}
第4步:使用!(包括随机示例)
List<IMammal> pets = new List<IMammal>()
{
new Cat(catValues),
new Gruffalo(gruffaloValues)
};
foreach(var pet in pets)
{
pet.Feed();
}
每只动物将由它们自己的实现来喂养。瞧,复杂的代码现在变得简单了。我还建议你阅读"头部优先设计模式",它解释了这个和许多其他概念。http://www.amazon.co.uk/Head-First-Design-Patterns-Freeman/dp/0596007124
如果您不介意创建类型映射,您可以像这样使用双重分派:
[EDIT]这个新的改进版本更好地处理子类。如果你有一个从另一个哺乳动物类派生的类(比如下面的例子中从Dog
派生的Pug
),那么你不需要显式地为Pug
类添加一个喂食器——它会自动调用它的基类Dog
的喂食器。
但是如果您愿意,您可以为派生类提供一个特定的馈线,如下面的Manx
类所示。
使用dynamic
要容易得多!我只是想展示一下,如果你不使用dynamic
,它会是什么样子。
using System;
using System.Collections.Generic;
namespace Demo
{
public class Mammal {}
public class Cat: Mammal {}
public class Pig: Mammal {}
public class Dog: Mammal {}
public class Pug: Dog {}
public class Manx: Cat {}
public static class Feeder
{
static readonly Dictionary<Type, Action<Mammal>> map = createMap();
static Dictionary<Type, Action<Mammal>> createMap()
{
return new Dictionary<Type, Action<Mammal>>
{
{typeof(Cat), mammal => GiveFood((Cat) mammal)},
{typeof(Dog), mammal => GiveFood((Dog) mammal)},
{typeof(Manx), mammal => GiveFood((Manx) mammal)}
};
}
public static void GiveFood(Mammal mammal)
{
for (
var currentType = mammal.GetType();
typeof(Mammal).IsAssignableFrom(currentType);
currentType = currentType.BaseType)
{
if (map.ContainsKey(currentType))
{
map[currentType](mammal);
return;
}
}
DefaultGiveFood(mammal);
}
public static void DefaultGiveFood(Mammal mammal)
{
Console.WriteLine("Feeding an unknown mammal.");
}
public static void GiveFood(Cat cat)
{
Console.WriteLine("Feeding the cat.");
}
public static void GiveFood(Manx cat)
{
Console.WriteLine("Feeding the Manx cat.");
}
public static void GiveFood(Dog dog)
{
Console.WriteLine("Feeding the dog.");
}
}
class Program
{
void test()
{
feed(new Cat());
feed(new Manx());
feed(new Dog());
feed(new Pug());
feed(new Pig());
feed(new Mammal());
}
void feed(Mammal mammal)
{
Feeder.GiveFood(mammal);
}
static void Main()
{
new Program().test();
}
}
}
如果不止一种动物具有相同的摄食行为,我建议使用策略模式将摄食行为封装在一个接口中,并具体实现每一组动物的每种行为
你将使用组合而不是继承
检查头部优先的设计模式,我认为这将是一个很好的实现在你的情况下