I have a base class. How can I name the correct method for the right derived class?

Obviously, we are trying to simplify this problem. I have a base class and several derived classes:

public class Mammal { } public class Cat : Mammal { } public class Dog : Mammal { } 

And utility class:

 public static class AnotherClass { public static void GiveFood(Cat cat) {} public static void GiveFood(Dog dog) {} } 

Somewhere else there is a Feed method that accepts a Mammal, and from the inside I want to call the correct overload in AnotherClass:

 public void Feed(Mammal mammal) { // if mammal is a cat, call the AnotherClass.GiveFood overload for cat, // if it a dog, call the AnotherClass.GiveFood for dog, etc. } 

One way to do this is to do something like:

 public void Feed(Mammal mammal) { if (mammal is dog) AnotherClass.GiveFood((Dog)mammal); if (mammal is Cat) AnotherClass.GiveFood((Cat)mammal); } 

... but I actually have a huge number of animals derived from mammals. Is there a better way to do what I want to do in Feed ()? Is there a way to avoid Feed () being a huge ugly method filled with these "if x is y then call z" -statements?

+4
source share
5 answers

I usually don't like to use dynamic , but this is one of those cases where I find this suitable:

 public void Feed(Mammal mammal) { Anotherclass.GiveFood((dynamic)mammal); } 

This will solve the correct overload at runtime without knowing the type in advance.

Strictly speaking, this is probably not the fastest way, but, as you point out, alternatives can be a real pain to maintain and / or difficult to read. In this case, the dynamic sending is elegant and will automatically include any overloads that you add in the future.

As Chris Sinclair points out, you can also add a catchall method to detect any invalid calls and provide a more friendly exception than the run-time error you would get if you could not find GiveFood() overload:

 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 + "."); } } 
+8
source

I think that this is the responsibility of animals for food processing, and not from the feeder. Otherwise, you will encounter the problem that you currently have:

 public void Feed(Mammal mammal) { if (mammal is Duck) { ((Duck)mammal).PryOpenBeak(); ((Duck)mammal).InsertFeedingTube(); ((Duck)mammal).PourDownFood(); } } 

And so on, although ducks are not mammals.

In any case, your Mammal class must have an abstract Feed(Food food) method, and the animal itself will have to figure out how to process the food. Thus, if you later add a new mammal, you do not have to update the feeder using the feeding logic for this new mammal.

@ Critical comment: then the animal can implement the correct IFoodXEater interface that contains the Feed(IFoodX) method, and then the feeder may look like this, although then you will return to the square:

 if (mammal is IFishEater) { ((IFishEater)mammal).Feed(new Fish()); } 
+5
source

If you don't mind creating a type map, you can fake a double dispatch as follows:

[EDIT] This new improved version is better suited for subclassing. If you have a class derived from another mammalian class (for example, Pug obtained from Dog in the example below), you do not need to explicitly add a feeder for the Pug class - it will automatically call the feeder for its base class, Dog .

But you can have a specific feeder for the derived class if you want, as evidenced by the Manx class below.

Using dynamic much easier! I just wanted to show how this might look if you are not using 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(); } } } 
+2
source

My recommendation:

Step 1 : Create the IMammal Interface

 <!-- language: c# --> public interface IMammal { void Feed(); } 

Step 2 : (Optional) Deploying the BaseMammal Base Class

 public class BaseMammal : IMammal { public void Feed() { Trace.Write("basic mammal feeding"); //a basic implementation of feeding, common to all or most mammals } } 

Step 3 : Implement Your Inherited Classes

 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 } } 

Step 4 : use! (including a random example)

 List<IMammal> pets = new List<IMammal>() { new Cat(catValues), new Gruffalo(gruffaloValues) }; foreach(var pet in pets) { pet.Feed(); } 

Each animal will feed on its own realization. Lo and lo - your complex code is now simple. I also recommend that you read β€œHead First Design Patterns,” which explains this and many other concepts. http://www.amazon.co.uk/Head-First-Design-Patterns-Freeman/dp/0596007124

+2
source

If more than one animal shares feeding behavior, I suggest using a strategy template to encapsulate feeding behavior in the interface, and a specific application of each behavior for each group of animals

you will use composition instead of inheritance

check out the initial head design patterns for this, I think it will be a good implementation in your case

+1
source

Source: https://habr.com/ru/post/1482433/


All Articles