我发现自己经常在格式之间进行转换,因此我提出了下面的转换框架。转换器接口表面显示从源类型转换到目标类型的方法。
public interface IConverter<in TSource, out TTarget>
{
TTarget Convert(TSource source);
}该工厂生产混凝土转换器:
public interface IConverterFactory
{
IConverter<TSource, TTarget> GetConverter<TSource, TTarget>();
}下一个接口允许实现泛型转换器,您可以将其插入到类中并在许多不同类型的转换中重用(您可以将工厂注入,但代码在泛型转换器中看起来更清晰):
public interface IGenericConverter
{
TTarget Convert<TSource, TTarget>(TSource source);
}这里有一个简单的例子来说明这些接口是如何实现的。首先,我将查看工厂,因为我们可以提供一个相当通用的工厂实现。
public class ConverterFactory : IConverterFactory
{
private readonly Dictionary<Tuple<Type, Type>, Func<object>> _converters =
new Dictionary<Tuple<Type, Type>, Func<object>>();
public void RegisterConverter<TSource, TTarget>(Func<object> constructor)
{
_converters.Add(new Tuple<Type, Type>(typeof(TSource), typeof(TTarget)), constructor);
}
public IConverter<TSource, TTarget> GetConverter<TSource, TTarget>()
{
var constructor = _converters[new Tuple<Type, Type>(typeof (TSource), typeof (TTarget))];
return (IConverter<TSource, TTarget>) constructor();
}
}工厂实现引入了一个字典来跟踪已注册的转换器和转换器的相关构造函数。增加了一种注册方法,以允许对转换器进行注册。最后实现了GetConverter,从字典中查找转换器。
泛型转换器可以通过依赖于此工厂来实现,如下所示:
public class GenericConverter : IGenericConverter
{
private readonly ConverterFactory _factory;
public GenericConverter(ConverterFactory factory)
{
_factory = factory;
}
public TTarget Convert<TSource, TTarget>(TSource source)
{
var converter = _factory.GetConverter<TSource, TTarget>();
return converter.Convert(source);
}
}将一个工厂注入构造函数,然后可以调用Convert进行任何转换,该类将从工厂获得一个转换器,运行转换器并返回转换结果。
这是一些模型。请注意POCO模型,没有依赖项,签名和清洁程度完全不同。
public class Dog
{
public string Name { get; set; }
}
public class DogDescriptor
{
public string Name { get; set; }
}
public class Cat
{
public int Number { get; set; }
}
public class CatDescriptor
{
public int Number { get; set; }
}因此,我们的计划是从DogDescriptor转换到Dog,从CatDescriptor转换到Cat。转换器需要实现IConverter。
public class DogConverter : IConverter<DogDescriptor, Dog>
{
public Dog Convert(DogDescriptor descriptor)
{
return new Dog {Name = descriptor.Name};
}
}
public class CatConverter : IConverter<CatDescriptor, Cat>
{
public Cat Convert(CatDescriptor descriptor)
{
return new Cat {Number = descriptor.Number};
}
}现在,我们有了进行转换所需的所有零碎:
var factory = new ConverterFactory();
// register converters
factory.RegisterConverter<DogDescriptor, Dog>(() => new DogConverter());
factory.RegisterConverter<CatDescriptor, Cat>(() => new CatConverter());
var converter = new GenericConverter(factory);
var dog = converter.Convert<DogDescriptor, Dog>(new DogDescriptor{Name = "Spot"});
var cat = converter.Convert<CatDescriptor, Cat>(new CatDescriptor{Number = 666});理想情况下,您将使用依赖注入将泛型转换器注入到任何需要进行转换的类中。然后,可以将依赖项注入解决方案配置为作为单例运行泛型转换器。
假设引入了Duck和DuckDescriptor这两个新模型,扩展转换支持是相当容易的。开发人员所需要做的就是实现一个DuckDescriptor到-Duck转换器,然后向工厂注册转换器。
工厂没有依赖项,可以很容易地进行测试。通用转换器需要一个模拟工厂进行测试。转换器只依赖于它们转换的模型,这使得测试转换器非常容易。
ToType样式方法扩展模型类。这种方法有什么好处吗?虽然我可以看到这种方法的吸引力,但它需要对每个转换都使用一个扩展方法,当您必须对相同模型进行特定于供应商的转换时,这种方法就会变得越来越难看。使用工厂解决方案,您可以简单地实现特定于供应商的工厂,并注入一个由特定于供应商的工厂构造的通用转换器。
发布于 2014-05-28 19:38:10
我只浏览了写得很好的问题(抱歉太晚了!)但是:
我在重新发明方向盘吗?我在.NET框架中找不到任何类似的东西,但我也不太努力。
据我所知,是的。
我相信你在这里所做的就是在类型之间注册一个“映射”。提供一种类型的实例,然后返回另一种类型的实例。
像自动机这样的库可以通过定义类型之间的映射来解决这个问题。
在您的示例中,您调用了converter.Convert,而典型的自动配置可能调用类似于Mapper.Map<T>(myType)的内容并执行类似的角色。如果我漏掉了什么就告诉我,但我想这就是你想要的。
https://codereview.stackexchange.com/questions/51889
复制相似问题