IOC容器内的工厂 - Ninject

本文关键字:工厂 Ninject IOC | 更新日期: 2023-09-27 18:31:56

我正在阅读并了解.Net中的DependencyInjection。 我正在开发一个新项目并重构大量代码,在大多数情况下,它非常令人兴奋和有希望。 我现在正在尝试弄清楚如何使用工厂(在 Ninject 中 - 但此时任何 IOC 容器都可以工作)。

在下面的代码中,我正在用其他一些代码试水,并对这个概念感到满意。 但是,当我重构代码时,我有点坚持使用一种干净的方法来执行此操作。 如您所见,有很多重复。 另外,我不确定我是否应该将工厂塞进 IOC 容器中(并且不知道该怎么做),或者是否可以在 CompositionRoot 之外进行创建。 唯一真正重要的事情是 MeasurementType 枚举,因为其他代码依赖于此级别的区分类型。

有人可以指出我正确的方向吗? 我在网上看过,显然没有把它们放在一起。

public enum MeasurementTypes
{
    Position,
    Distance,
    Altitude,
    Velocity,
    Clock
}
public enum VelocityTypes
{
    [Description("meters / second")]
    MetersPerSecond,
    [Description("knots")]
    Knots
}
public enum DistanceTypes
{
    [Description("meters")]
    Meters,
    [Description("Nautical Miles")]
    NauticalMiles,
    [Description("Statute Miles")]
    StatuteMiles
}
public class UnitOfMeasurementFactory
{
    public static UnitOfMeasurement CreateUnitOfMeasurement(Enum type, string name = "anonymous", double value = 0)
    {
        if (type is VelocityTypes)
        {
            return VelocityUnitOfMeasurementFactory.CreateVelocityUnitOfMeasurement((VelocityTypes)type, name, value);
        }
        else if (type is DistanceTypes)
        {
            return DistanceUnitOfMeasurementFactory.CreateDistanceUnitOfMeasurement((DistanceTypes) type, name,
                                                                                    value);
        }
        throw new NotImplementedException();
    }
}
public class VelocityUnitOfMeasurementFactory
{
    public static UnitOfMeasurement CreateVelocityUnitOfMeasurement(VelocityTypes velocityType, string name, double value)
    {
        UnitOfMeasurement uom;
        switch (velocityType)
        {
            case VelocityTypes.Knots:
                uom = new Knots(name, value);
                break;
            case VelocityTypes.MetersPerSecond:
                uom = new MetersPerSecond(name, value);
                break;
            default:
                uom = new MetersPerSecond(name, value);
                break;
        }
        return uom;
    }
}
public class DistanceUnitOfMeasurementFactory
{
    public static UnitOfMeasurement CreateDistanceUnitOfMeasurement(DistanceTypes distanceType, string name,
                                                                    double value)
    {
        UnitOfMeasurement uom;
        switch (distanceType)
        {
            case DistanceTypes.Meters:
                uom = new Meters(name, value);
                break;
            case DistanceTypes.NauticalMiles:
                uom = new NauticalMiles(name, value);
                break;
            case DistanceTypes.StatuteMiles:
                uom = new StatuteMiles(name, value);
                break;
            default:
                uom = new Meters(name, value);
                break;
        }
        return uom;
        }
}
public sealed class Knots : UnitOfMeasurement
{
    public Knots(string name, double value)
    {
        MeasurementType = MeasurementTypes.Velocity;
        RoundingDigits = 5;
        ConvertToBaselineFactor = .514444;
        ConvertFromBaselineFactor = 1.94384;
        Name = name;
        Value = value;
        Units = "knots";
    }
}
public sealed class Meters : UnitOfMeasurement
{
    public Meters(string name, double value)
    {
        MeasurementType = MeasurementTypes.Distance;
        RoundingDigits = 5;
        ConvertToBaselineFactor = 1.0;
        ConvertFromBaselineFactor = 1.0;
        Name = name;
        Value = value;
        Units = "m";
    }
}

IOC容器内的工厂 - Ninject

我会做这样的事情。

interface IUnitOfMeasurementFactory
{
    T Create<T>(string name, double value) where T: UnitOfMeasurement;
}
class Program
{
    static void Main(string[] args)
    {
        var kernel = new StandardKernel();
        kernel.Bind<IUnitOfMeasurementFactory>().ToFactory();
        var factory = kernel.Get<IUnitOfMeasurementFactory>();
        var meters = factory.Create<Meters>("myDistance", 123.12);
        var knots = factory.Create<Knots>("mySpeed", 345.21)
    }
}

它跳过了"测量类型"概念,但您可以通过Meters实现IDistanceUnitOfMeasurementKnots实现IVelocityUnitOfMeasurement来管理它。

我会同意@shamp00的答案,但在这里你有一个你做事方式的工作实现:

using System.ComponentModel;
using System.Globalization;
using System.Linq;
using FluentAssertions;
using Ninject;
using Ninject.Activation;
using Ninject.Parameters;
using Ninject.Syntax;
using Xunit;
public class NinjectFactoryTest
{
    [Fact]
    public void Test()
    {
        var kernel = new StandardKernel();
        kernel.Bind<IUnitOfMeasurementFactory>().To<UnitOfMeasurementFactory>();
        kernel.Bind<UnitOfMeasurement>().To<Knots>()
            .WhenClassifiedBy(VelocityUnitOfMeasurementFactory.BuildClassification(VelocityTypes.Knots));
        kernel.Bind<UnitOfMeasurement>().To<Meters>()
            .WhenClassifiedBy(DistanceUnitOfMeasurementFactory.BuildClassification(DistanceTypes.Meters));
        const string ExpectedName = "hello";
        const double ExpectedValue = 5.5;
        var actualUnitOfMeasurement = kernel.Get<VelocityUnitOfMeasurementFactory>()
            .CreateVelocityUnitOfMeasurement(VelocityTypes.Knots, ExpectedName, ExpectedValue);
        actualUnitOfMeasurement.Should().BeOfType<Knots>();
        actualUnitOfMeasurement.Name.Should().Be(ExpectedName);
        actualUnitOfMeasurement.Value.Should().Be(ExpectedValue);
    }
}
public class ClassifiedParameter : Parameter
{
    public ClassifiedParameter(string classification)
        : base("Classification", ctx => null, false)
    {
        this.Classification = classification;
    }
    public string Classification { get; set; }
}
public static class ClassifiedBindingExtensions
{
    public static IBindingInNamedWithOrOnSyntax<T> WhenClassifiedBy<T>(this IBindingWhenSyntax<T> syntax, string classification)
    {
        return syntax.When(request => request.IsValidForClassification(classification));
    }
    public static bool IsValidForClassification(this IRequest request, string classification)
    {
        ClassifiedParameter parameter = request
            .Parameters
            .OfType<ClassifiedParameter>()
            .SingleOrDefault();
        return parameter != null && classification == parameter.Classification;
    }
}
public enum MeasurementTypes
{
    Position,
    Distance,
    Altitude,
    Velocity,
    Clock
}
public enum VelocityTypes
{
    [Description("meters / second")]
    MetersPerSecond,
    [Description("knots")]
    Knots
}
public enum DistanceTypes
{
    Meters,
    NauticalMiles,
    StatuteMiles
}
public interface IUnitOfMeasurementFactory
{
    UnitOfMeasurement Create(string classification, string name, double value);
}
internal class UnitOfMeasurementFactory : IUnitOfMeasurementFactory
{
    public const string ClassificationTemplate = "{0}://{1}";
    private readonly IResolutionRoot resolutionRoot;
    public UnitOfMeasurementFactory(IResolutionRoot resolutionRoot)
    {
        this.resolutionRoot = resolutionRoot;
    }
    public UnitOfMeasurement Create(string classification, string name, double value)
    {
        return this.resolutionRoot.Get<UnitOfMeasurement>(
            new ClassifiedParameter(classification),
            new ConstructorArgument("name", name),
            new ConstructorArgument("value", value));
    }
}
public class DistanceUnitOfMeasurementFactory
{
    private readonly IUnitOfMeasurementFactory factory;
    public DistanceUnitOfMeasurementFactory(IUnitOfMeasurementFactory factory)
    {
        this.factory = factory;
    }
    public static string BuildClassification(DistanceTypes distanceType)
    {
        return string.Format(
            CultureInfo.InvariantCulture,
            UnitOfMeasurementFactory.ClassificationTemplate,
            MeasurementTypes.Distance.ToString(),
            distanceType.ToString());
    }
    public UnitOfMeasurement CreateDistanceUnitOfMeasurement(DistanceTypes distanceType, string name, double value)
    {
        string classification = BuildClassification(distanceType);
        return this.factory.Create(classification, name, value);
    }
}
public class VelocityUnitOfMeasurementFactory
{
    private readonly IUnitOfMeasurementFactory factory;
    public VelocityUnitOfMeasurementFactory(IUnitOfMeasurementFactory factory)
    {
        this.factory = factory;
    }
    public static string BuildClassification(VelocityTypes velocityType)
    {
        return string.Format(
            CultureInfo.InvariantCulture,
            UnitOfMeasurementFactory.ClassificationTemplate,
            MeasurementTypes.Velocity.ToString(),
            velocityType.ToString());
    }
    public UnitOfMeasurement CreateVelocityUnitOfMeasurement(VelocityTypes velocityType, string name, double value)
    {
        string classification = BuildClassification(velocityType);
        return this.factory.Create(classification, name, value);
    }
}
public abstract class UnitOfMeasurement
{
    public MeasurementTypes MeasurementType { get; set; }
    public int RoundingDigits { get; set; }
    public string Name { get; set; }
    public double Value { get; set; }
    public string Units { get; set; }
}
public sealed class Knots : UnitOfMeasurement
{
    public Knots(string name, double value)
    {
        MeasurementType = MeasurementTypes.Velocity;
        RoundingDigits = 5;
        Name = name;
        Value = value;
        Units = "knots";
    }
}
public sealed class Meters : UnitOfMeasurement
{
    public Meters(string name, double value)
    {
        MeasurementType = MeasurementTypes.Distance;
        RoundingDigits = 5;
        Name = name;
        Value = value;
        Units = "m";
    }
}

提示:它使用 xunit([Fact] 属性),但您可以轻松地用 Main 方法替换它。当然,您还可以删除FluentAssertions。