Для данного типа T разрешите и используйте другой объект универсального типа Calc <T>для обработки T - PullRequest
0 голосов
/ 09 мая 2018

Извините, если название не очень хорошо объясняет вопрос, но я не могу придумать лучшего названия. Это может оказаться довольно длинным вопросом, но, пожалуйста, потерпите меня.

Допустим, у меня есть два типа транспортных средств Car и Yacht, которые расширяют интерфейс под названием IVehicle.

Сам интерфейс не имеет большого значения для этого вопроса, но классы содержат свойства, описывающие их.

У меня тогда есть ITaxCalculator<T> where T : IVehicle, который содержит метод double Calculate(T vehicle);.

Затем существует несколько классов, которые реализуют разные версии ITaxCalculator<T> (и принимают разные аргументы конструктора), такие как:

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

Затем у меня есть List<IVehicle>, содержащее мои несколько машин и яхт, и я хочу рассчитать общую сумму налогов, которые мне придется заплатить за них, и в то же время иметь возможность переключаться на используемый метод. рассчитать налоги каждого типа.


Вот код:

Интерфейсы:

ITaxCalculator<T>

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

IVehicle

public interface IVehicle
{
    string RegistrationPlate { get; }
}

Реализация

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

Главная

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

Я пытался решить эту проблему разными способами, например

  • Контейнеры IoC
  • Гораздо более легкий вариант "Resolver", где вы регистрируете движки в классе, содержащем Dictionary<Type, object>, где object - это ITaxCalculators, зарегистрированные и разрешенные с помощью таких методов, как
    1. public ITaxCalculator<T> GetTaxCalculator<T>() where T : IVehicle
    2. public void RegisterTaxCalculator<T>(ITaxCalculator<T> calculator) where T : IPosition
  • Передача ITaxCalculator каждому экземпляру T во время его создания и возможность использования T для расчета собственных налогов.

Все они в конечном итоге чувствуют себя чрезмерно сложными, применяя плохие методы или требуя слишком много шаблонов. Поэтому мне интересно, как лучше всего это структурировать? Я ошибаюсь с самого начала или есть какой-то паттерн, о котором я еще не думал?

Спасибо! * * 1092

1 Ответ

0 голосов
/ 09 мая 2018

По сути, вам нужен способ определения сопоставления между налоговым калькулятором и транспортным средством.

Прежде всего, сделайте налоговый калькулятор не универсальным:

public interface ITaxCalculator
{
    double Calculate(IVehicle vehicle);
}

Гораздо проще обнаружить все неуниверсальные реализации, загрузить их в коллекцию и вызвать Calculate.

Чтобы разобраться с конкретными деталями реализации IVehicle, объявите базовый класс для налоговых калькуляторов и сделайте it generic:

public abstract class TaxCalculator<T> : ITaxCalculator
    where T : IVehicle
{
    public double Calculate(IVehicle vehicle)
    {
        // this is a single place, where cast is required
        return Calculate((T)vehicle);
    }

    protected abstract double Calculate(T vehicle);
}

Задача картирования приводит нас к метаданным.
Большинство DI-контейнеров имеют эту функцию. Например, вот Autofac документы.

Определить класс метаданных:

// This is metadata class;
// It defines vehicle type to calculate the tax value
public class TaxCalculatorMetadata
{
    public Type VehicleType { get; set; }
}

Регистрация ITaxCalculator реализации:

// Every implementation of ITaxCalculator must be registered like this
builder.RegisterType<CarTaxCalculator>()
    .As<ITaxCalculator>()
    .WithMetadata<TaxCalculatorMetadata>(m => m.For(_ => _.VehicleType, typeof(Car)));

Теперь вы можете загрузить все ITaxCalculator реализации, как-то отфильтровать их («вставить их в« слоты »») и получить конкретный калькулятор, используя тип транспортного средства из «слота»:

var vehicles = new List<Vehicle>
{
    // ...
};

// assuming, that tax calculators were imported  
// using IEnumerable<Lazy<ITaxCalculator, TaxCalculatorMetadata>>
foreach (var vehicle in vehicles)
{
    var taxCalculator = taxCalculators
        .First(_ => _.Metadata.VehicleType == vehicle.GetType());

    Console.WriteLine(taxCalculator.Value.Calculate(vehicle));
}
...