2014-01-06 104 views
5

我想创建一个可以像这样使用流利的接口:如何创建一个良好的接口与泛型

void Main() { 
    ModelStateMappings.MapDomainModel<Book>().MapViewModel<BookViewModel>() 
     .Properties(book => book.Author, vm => vm.AuthorsName) 
     .Properties(book => book.Price, vm => vm.BookPrice); 

    ModelStateMappings.MapDomainModel<Store>().MapViewModel<StoreViewModel>() 
     .Properties(store => store.Owner, vm => vm.OwnersName) 
     .Properties(store => store.Location, vm => vm.Location); 
} 

我想用一个看起来像这样的集合结束:

static class ModelStateaMappings { 
    private static IList<ModelMappings> mappings; 
    // other methods in here to get it working 
} 

class ModelMappings { 
    public Type DomainModelType {get;set;} 
    public Type ViewModelType {get;set;} 
    public IList<PropertyMapping> PropertyMappings {get;set;} 
} 

class PropertyMapping { 
    public Expression<Func<object, object>> DomainProperty {get;set;} 
    public Expression<Func<object, object>> ViewModelProperty {get;set;} 
} 

我没有得到上述成绩,但I did create something similar以类似的方式工作,但我不特别喜欢我如何设置流畅的接口。我宁愿让它像我上面那样阅读。

+0

你现在的接口是什么样的?具体问题是什么让你看起来不像你想要的那样? –

+0

我无法弄清楚如何将泛型推断一直引入.Properties()方法,无需同时传入两种类型。我想这样做的方式是每次传递一个通用的方法,然后将它们放在Property()方法中 –

+0

声音就像你需要一个类型数组。你看过[Automapper?](https://github.com/AutoMapper/AutoMapper) –

回答

2

你可以用下面的代码实现它

static class ModelStateMappings 
{ 
    public static DomainModelMapping<TDomainModel> MapDomainModel<TDomainModel>() 
    { 
     // edit the constructor to pass more information here if needed. 
     return new DomainModelMapping<TDomainModel>(); 
    } 
} 

public class DomainModelMapping<TDomainModel> 
{ 
    public ViewModelMapping<TDomainModel, TViewModel> MapViewModel<TViewModel>() 
    { 
     // edit the constructor to pass more information here if needed. 
     return new ViewModelMapping<TDomainModel, TViewModel>(); 
    } 
} 

public class ViewModelMapping<TDomainModel, TViewModel> 
{ 
    public ViewModelMapping<TDomainModel, TViewModel> 
     Properties<TDomainPropertyType, TViewModelPropertyType>(
      Expression<Func<TDomainModel, TDomainPropertyType>> domainExpr, 
      Expression<Func<TViewModel, TViewModelPropertyType>> viewModelExpr) 
    { 
     // map here 
     return this; 
    } 
} 

你不必因为它们已经记住作为返回类型的泛型参数指定所有先前设置的泛型类型。对于Properties方法调用的通用参数可以被跳过,因为它们将由编译器推断。而且你比任何地方都使用object打字更好。

当然这是最简单的版本。您可以在这些类型之间传递更多信息,因为您指定了如何创建下一个必需的类型。

它也调用MapViewModel而不要求MapDomainModel第一次不可能(只要你使构造函数和关闭所有在单独的DLL),什么应该是一件好事。

4

有两种常见的方法来创建流畅的界面。

一种方法是添加到正在构建的类的当前实例,并从每种方法返回this

事情是这样的:

public class NamesBuilder 
{ 
    private List<string> _names = new List<string>(); 
    public NamesBuilder AddName(string name) 
    { 
     _names.Add(name); 
     return this; 
    } 
} 

用这种生成器的问题是,你可以写很容易bug的代码:

var namesBuilder = new NamesBuilder(); 

var namesBuilder1 = namesBuilder.AddName("John"); 
var namesBuilder2 = namesBuilder.AddName("Jack"); 

如果我看到这个代码,我会期望namesBuilder1namesBuilder2每个人只有一个名字,而namesBuilder不会有任何名字。然而,由于它们是相同的实例,因此实现将在所有三个变量中都有名称。

实现流畅接口的更好方法是在构建器类上创建一个链,对其进行延迟评估,以便在构建完成后创建最终类。那么如果你在建造过程的中间分支,你可能会犯一个错误。

这是我会想到写那种代码:

var bookMap = 
    ModelStateMappings 
     .Build<Book, BookViewModel>() 
     .AddProperty(book => book.Author, vm => vm.AuthorsName) 
     .AddProperty(book => book.Price, vm => vm.BookPrice) 
     .Create(); 

var bookStore = 
    ModelStateMappings 
     .Build<Store, StoreViewModel>() 
     .AddProperty(store => store.Owner, vm => vm.OwnersName) 
     .AddProperty(store => store.Location, vm => vm.Location) 
     .Create(); 

,使这项工作的代码是比“名”的例子更复杂一点。

public static class ModelStateMappings 
{ 
    public static Builder<M, VM> Build<M, VM>() 
    { 
     return new Builder<M, VM>(); 
    } 

    public class Builder<M, VM> 
    { 
     public Builder() { } 

     public Builder<M, VM> AddProperty<T>(
      Expression<Func<M, T>> domainMap, 
      Expression<Func<VM, T>> viewModelMap) 
     { 
      return new BuilderProperty<M, VM, T>(this, domainMap, viewModelMap); 
     } 

     public virtual Map Create() 
     { 
      return new Map(); 
     } 
    } 

    public class BuilderProperty<M, VM, T> : Builder<M, VM> 
    { 
     private Builder<M, VM> _previousBuilder; 
     private Expression<Func<M, T>> _domainMap; 
     private Expression<Func<VM, T>> _viewModelMap; 

     public BuilderProperty(
      Builder<M, VM> previousBuilder, 
      Expression<Func<M, T>> domainMap, 
      Expression<Func<VM, T>> viewModelMap) 
     { 
      _previousBuilder = previousBuilder; 
      _domainMap = domainMap; 
      _viewModelMap = viewModelMap; 
     } 

     public override Map Create() 
     { 
      var map = _previousBuilder.Create(); 
      /* code to add current map to Map class */ 
      return map; 
     } 
    } 
} 

这种类型的构建器的另一个好处是,你还维护强类型的属性字段。

当然,您需要为Create方法中的映射输入正确的代码。