I am in a situation where I am iterating a List<Animal>
and want a Handler
class to handle each element according to its derived type. The way I'm doing it is weird and hard to follow. Is there a better way?
The purpose of the Handler class is to encapsulate the code for talking to the database, I don't want the animals to have any reference to it. I just want to point a hose of animals at the database and have square pegs go in square holes, and round pegs in round holes.
Thanks in advance.
abstract class Animal
{
public void update() { }
public virtual void Handle(Handler handler) { }
}
public class Dog : Animal
{
public override void Handle(Handler handler) => handler.Handle(this);
}
public class Cat : Animal
{
public override void Handle(Handler handler) => handler.Handle(this);
}
public class Handler
{
private Clinic clinic; //only the handler talks to the database
public void Handle(Animal animal) => animal.Handle(this);
public void Handle(Dog dog) { /* write dog fields to database */ }
public void Handle(Cat cat) { /* write cat fields to database */ }
}
public void main()
{
List<Animal> animals = getAnimals();
Handler handler = new Handler();
foreach(var animal in animals)
{
handler.handle(animal);
}
}
Edit: I think my question is essentially answered here: Passing different type of objects through the same method.
It seems like instead of having a sender system that he passes data to, it's like he is taking data and building a system around it. That kind of structure would incorporate design principle violations no matter what you tried to build around it.
I think the answer is to move the database write into the factory that generates the animals. This means I can't prevalidate the entire set of animals before writing, but it cleans out all the indirection and potentially removes the animal and handler classes entirely.
In C#, starting from version 7.0, you can make use of pattern matching to handle each concrete instance separately.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public abstract class Animal
{
public void update() { }
public virtual void Handle(Handler handler) { }
}
public class Dog : Animal {}
public class Cat : Animal {}
public class Handler
{
public void Handle(List<Animal> animals)
{
foreach (var animal in animals)
{
switch (animal)
{
case Cat cat:
Handle(cat);
break;
case Dog dog:
Handle(dog);
break;
default:
throw new NotImplementedException();
}
}
}
public void Handle(Dog dog) { Console.WriteLine("Wrote a dog field"); }
public void Handle(Cat cat) { Console.WriteLine("Wrote a cat field"); }
}
public static void Main()
{
List<Animal> animals = new List<Animal>(){
new Dog(),
new Cat(),
new Dog(),
};
Handler handler = new Handler();
handler.Handle(animals);
}
}
Alternatively you could use LINQ's OfType<T>
method to filter out instances of each concrete class and handle them that way.