I'm trying to make food for my IAnimals using a FoodFactory and have a large List<IAnimal> of Dogs and Foxs etc. Ideally, I would pass in an IAnimal and the FoodFactory would return the right food but I am not sure how to do this.
interface IAnimal {
void MakeNoise();
}
public sealed class Dog : IAnimal {
public void MakeNoise() { Console.WriteLine("Woof woof"); }
}
public sealed class Fox : IAnimal {
public void MakeNoise() { Console.WriteLine("What does the fox say?"); }
}
public static class FoodFactory {
public static void Create(Dog dog) {
Console.WriteLine("return dog food");
}
public static void Create(Fox fox) {
Console.WriteLine("return fox food");
}
}
and here's my caller code:
var myList = new List<IAnimal> { new Dog(), new Fox() };
foreach (var animal in myList)
{
FoodFactory.Create(animal); // Sadly, this doesn't work
}
Is there a 'proper' way to do this? All I can think of is a load of if statements like:
if (animal is Dog) { FoodFactory.Create(animal as Dog); }
I have a lot of animals and I'd much rather the computer work out which one it's dealing with than me! Thank you.
Thank you to all that have posted. I went with Xinchao's answer because it was most applicable in my context; it didn't affect my animal classes and allowed the type to be resolved at runtime without me checking all the animal types. There were three helpful answers which would have worked (e.g. using visitor pattern) but they weren't quite applicable in my context as I don't want to change/extend my animals.
The reason you are having this problem is the so-called overload resolution happens at compile time instead of run-time. There are a couple of ways to solve it: Using a bunch of if-else-if statements is certainly one way, although you can write it prettier.
With .Net 4.0 or above, you can use to "dynamic" keyword to delay the overload resolution to run-time. It has a performance penalty, but it achieves accurately what you need:
public static class FoodFactory {
public static void Create(dynamic animal) {
InternalCreate (animal);
}
private static void InternalCreate (Dog dog) {
Console.WriteLine("return dog food");
}
private static void InternalCreate (Fox fox) {
Console.WriteLine("return fox food");
}
}
To add a bit more type safety, you might consider to do something like:
public static class FoodFactory {
public static void Create(IAnimal animal) {
Dispatch (animal);
}
private static void Dispatch (dynamic animal) {
InternalCreate (animal);
}
private static void InternalCreate (Dog dog) {
Console.WriteLine("return dog food");
}
private static void InternalCreate (Fox fox) {
Console.WriteLine("return fox food");
}
}
Your FoodFactory does need to know what kind of animal its feeding, but I would imagine that the animal itself knows what kind of food it wants to eat. What if IAnimal provided that?
interface IAnimal {
void MakeNoise();
FoodType FavoriteFood { get; }
}
public sealed class Dog : IAnimal {
public void MakeNoise() { Console.WriteLine("Woof woof"); }
public FoodType FavoriteFood { get { return FoodType.DogFood; } }
}
public sealed class Fox : IAnimal {
public void MakeNoise() { Console.WriteLine("What does the fox say?"); }
public FoodType FavoriteFood { get { return FoodType.WhatTheFoxEats; } }
}
Now your FoodFactory has the information it needs to build food:
public static class FoodFactory {
public static IFood Create(IAnimal animal) {
// animal.FavoriteFood tells you what food to make
}
}
Maybe FavoriteFood actually returns an instance of food, and the factory just delegates to that? Maybe the factory maintains a mapping or some other logic of the enum above to actual food instances? Maybe some other way? (Or maybe you just output to the console like you already are instead of returning an object like I'm trying to do, in which case the animals can just write to the console and the factory still doesn't need to know anything else about the animal.) In any event, the factory's relationship to the animal is now polymorphically defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With