IOC Container内的工厂 - Ninject

时间:2013-09-19 16:08:46

标签: c# dependency-injection ninject ninject-extensions

我正在阅读并绕过.Net中的DependencyInjection。我正在开发一个新项目并重构很多代码,而且大多数情况下它非常令人兴奋和充满希望。我现在正在尝试研究如何使用工厂(在Ninject中 - 但任何IOC容器都可以在这一点上工作)。

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

有人能指出我正确的方向吗?我已经在网上看了,显然没有把它们整合在一起。

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

2 个答案:

答案 0 :(得分:2)

我会做这样的事情。

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

它会跳过“MeasurementTypes”概念,但您可以通过Meters实施IDistanceUnitOfMeasurementKnots实施IVelocityUnitOfMeasurement来管理它。

答案 1 :(得分:1)

我会选择@ 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,当然,你可以删除。