mercredi 9 mai 2018

Given type T, resolve and use other object of generic type Calc

Sorry if the title doesn't explain the question very well, but I couldn't think any better name. This might end up being a quite long question, but please bear with me.

Let's say I have two types of vehicles Car and Yacht which extend an interface called IVehicle.

The interface itself doesn't matter much for this question, but the classes contain properties describing them.

I then have a ITaxCalculator<T> where T : IVehicle which contains method double Calculate(T vehicle);.

There are then several classes which implement different versions of ITaxCalculator<T> (and take different constructor arguments), such as:

  • LegitYachtTaxCalculator : ITaxCalculator<Yacht>
  • TaxHavenYachtTaxCalculator : ITaxCalculator<Yacht>
  • CarTaxCalculator : ITaxCalculator<Car>

I then have a List<IVehicle> containing my multiple cars and yachts, and I want to calculate the total amount of taxes I'm going to have to pay for them while at the same time being able to switch out the method used to calculate each type's taxes.


Here's some code:

Interfaces:

ITaxCalculator<T>

public interface ITaxCalculator<T> where T : IVehicle
{
    double Calculate(T vehicle);
}

IVehicle

public interface IVehicle
{
    string RegistrationPlate { get; }
}

Implementations

Car

public class Car : IVehicle
{
    public Car(string registrationPlate)
    {
        RegistrationPlate = registrationPlate;
    }
    public string RegistrationPlate { get; }
}

Yacht

public class Yacht : IVehicle
{
    public int WeightInTons { get; }
    public Yacht(string registrationPlate, int weightInTons)
    {
        RegistrationPlate = registrationPlate;
        WeightInTons = weightInTons;
    }
    public string RegistrationPlate { get; }
}

CarTaxCalculator : ITaxCalculator<Car>

public class CarTaxCalculator : ITaxCalculator<Car>
{
    public double Calculate(Car vehicle)
    {
        Console.WriteLine($"Calculating tax for {vehicle.GetType().FullName} with plate {vehicle.RegistrationPlate} using {this.GetType().FullName}");

        return 4999.95;
    }
}

LegitYachtTaxCalculator : ITaxCalculator<Yacht>

public class LegitYachtTaxCalculator : ITaxCalculator<Yacht>
{
    public double WeightMultiplier { get; }
    public LegitYachtTaxCalculator(double weightMultiplier)
    {
        WeightMultiplier = weightMultiplier;
    }
    public double Calculate(Yacht vehicle)
    {
        Console.WriteLine($"Calculating tax for {vehicle.GetType().FullName} with plate {vehicle.RegistrationPlate} using {this.GetType().FullName}");

        return vehicle.WeightInTons * WeightMultiplier;
    }
}

TaxHavenYachtTaxCalculator : ITaxCalculator<Yacht>

public class TaxHavenYachtTaxCalculator : ITaxCalculator<Yacht>
{
    public double Calculate(Yacht vehicle)
    {
        Console.WriteLine($"Calculating tax for {vehicle.GetType().FullName} with plate {vehicle.RegistrationPlate} using {this.GetType().FullName}");

        return 0.0; // No taxes, woho!
    }
}

Main

static void Main(string[] args)
{
    List<IVehicle> vehicles = new List<IVehicle>
    {
        new Car("PLT111"),
        new Yacht("PLT333", 2500)
    };

    double totalTaxes = 0;

    foreach (var vehicle in vehicles)
    {
        //Here I want to use engines which are configurable earlier to calculate the taxes for the vehicles
    }
}


I've tried to solve this in many ways such as

  • IoC containers
  • A much lighter "Resolver" variant where you register engines in a class containing a Dictionary<Type, object> where object is ITaxCalculators registered and resolved through methods such as
    1. public ITaxCalculator<T> GetTaxCalculator<T>() where T : IVehicle
    2. public void RegisterTaxCalculator<T>(ITaxCalculator<T> calculator) where T : IPosition
  • Passing the ITaxCalculator to every instance of T during its creation, and letting T calculate its own taxes by using it.

They all end up feeling overly complex, using bad practices or requiring too much boilerplate. So I'm wondering what the best way would be to structure this? Am I on the wrong track from the start, or is there any pattern I haven't thought about yet?

Thanks!

Aucun commentaire:

Enregistrer un commentaire