2015-12-15 40 views
2

我想实现一个流畅的界面,让我指定一个表,其中表和列具有属性的列。我想能够像这样初始化表类:这个流畅的接口可以简化吗?

var t = TableBuilder.Create() 
        .WithName("test") 
        .WithColumn("col1").WithType("Int32") 
        .WithColumn("col2").WithType("String") 
        .WithColumn("col3"); 

我想出了一个解决方案,工作。我的问题是,实施可以简化,还是应该采取完全不同的方法?我在类似案例中看到了扩展方法的用法,但我不知道他们是否会帮助我或者在这种情况下如何使用它们。

下面是代码(下面的一些评论):

interface ITable 
{ 
    ITable WithName(string tableName); 
    ITableAndColum<ITable, IColumn> WithColumn(string columnName); 
} 

interface IColumn 
{ 
    IColumn WithType(string typeName); 
} 

interface ITableAndColum<TTable, TColumn> 
    where TTable : ITable 
    where TColumn : IColumn 
{ 
    ITableAndColum<ITable, IColumn> WithColumn(string columnName); 
    ITableAndColum<ITable, IColumn> WithType(string typeName); 
} 

class TableAndColumn : ITableAndColum<ITable, IColumn> 
{ 
    public ITable Table { get; set; } 
    public IColumn Column { get; set; } 

    public ITableAndColum<ITable, IColumn> WithColumn(string columnName) 
    { 
     return Table.WithColumn(columnName); 
    } 

    public ITableAndColum<ITable, IColumn> WithType(string typeName) 
    { 
     Column.WithType(typeName); 
     return this; 
    } 

    public override string ToString() 
    { 
     return Table.ToString(); 
    } 
} 

class Column : IColumn 
{ 
    public string ColumnName { get; set; } 
    public string TypeName { get; set; } 

    public IColumn WithType(string typeName) 
    { 
     TypeName = typeName; 
     return this; 
    } 
} 

class TableBuilder : ITable 
{ 
    public string TableName { get; set; } 
    List<Column> Columns; 

    TableBuilder() { Columns = new List<Column>(); } 

    public static ITable Create() { return new TableBuilder(); } 

    public ITable WithName(string tableName) 
    { 
     TableName = tableName; 
     return this; 
    } 

    public ITableAndColum<ITable, IColumn> WithColumn(string columnName) 
    { 
     Column thisColumn = new Column() { ColumnName = columnName }; 
     Columns.Add(thisColumn); 

     return new TableAndColumn() { Table = this, Column = thisColumn }; 
    } 

    public override string ToString() 
    { 
     return TableName + "(" + string.Join(",", Columns.Select(c => (c.TypeName != null ? c.TypeName + " " : "") + c.ColumnName)) + ")"; 
    } 
} 

class Example 
{ 
    public Example() 
    { 
     var t = TableBuilder.Create() 
          .WithName("test") 
          .WithColumn("col1").WithType("Int32") 
          .WithColumn("col2").WithType("String") 
          .WithColumn("col3"); 
     var tdef = t.ToString(); 
    } 

} 

有两个接口是ITableIColumn与那里实现TableColum。他们公开适合他们类型的适当方法。第三个接口ITableAndColumn结合了其他接口,它的实现TableAndColumn持有对表实例和列实例的引用。该界面公开了ITableIColumn的选定方法并调用相应的实例。

这种方法是否复杂?扩展方法是以这种方式吗?

+0

出于好奇,你为什么要反对使用设计您的API使用传统的风格? – Dai

+1

这个问题的答案会非常有偏见,但我的评论也是如此 - 作为个人喜好,我希望将'WithType'方法称为'AndType',以便获得'WithColumn(“col1”)。AndType “Int32”)'我宁愿使用实际的'Type's而不是字符串来定义类型。 –

+0

我想让我的API更具可读性。当然,只有在代码的正确缩进和换行符以及连锁不会太长时间的情况下才能正常工作,但我可以忍受这两种退步。我知道我可以使用更简单的方法链接构造函数初始值设定项和名称参数。但我仍然想知道一些API如何在我的示例代码中进行说明。 – Sascha

回答

2

我不是制作TableAndColumn的巨大粉丝。似乎对我来说错了。他们是不同的实体。我已经采取了首页输出KendoUI在其流畅的造型方面:

TableBuilder.Create() 
    .WithName("test") 
    .WithColumn(c => c.WithName("col1").WithType("Int32")) 
    .WithColumn(c => c.WithName("col2").WithType("String")) 
    .WithColumn(c => c.WithName("col3")); 

注意我们如何通过一个lambda配置列。这使我们能够正确地限制IColumn可以暴露的内容。

当然,你可以把它改成:WithColumn("col1", c => c.WithType("Int32"))和过载.WithColumn("col3")

实现:

interface ITable 
{ 
    ITable WithColumn(Action<IColumn> c); 
    ITable WithName(string tableName); 
} 

interface IColumn 
{ 
    IColumn WithName(string columnName); 
    IColumn WithType(string typeName); 
} 

class TableBuilder : ITable 
{ 
    public string TableName { get; set; } 
    List<Column> Columns = new List<Column>(); 

    public static ITable Create() { return new TableBuilder(); } 

    public ITable WithName(string tableName) 
    { 
     TableName = tableName; 
     return this; 
    } 

    public ITable WithColumn(Action<IColumn> c) 
    { 
     Column thisColumn = new Column(); 
     c(thisColumn); 
     Columns.Add(thisColumn); 

     return this; 
    } 
} 

class Column : IColumn 
{ 
    public string ColumnName { get; set; } 
    public string TypeName { get; set; } 

    public IColumn WithName(string columnName) 
    { 
     ColumnName = columnName; 
     return this; 
    } 

    public IColumn WithType(string typeName) 
    { 
     TypeName = typeName; 
     return this; 
    } 
} 
+1

是的,TableAndColumn是解决单独类的方法的一种解决方法,并让组合类选择要调用的方法。这是我想简化我的方法的原因之一。你的建议很好地解决了这个问题,但它使得代码更不可读(恕我直言)。 – Sascha

+1

@Sascha是的,我同意。但是,我的'TableAndColumn'的主要问题是可能会有另一个子属性(比如'行',即使这里没有意义)。然后你需要'TableAndRow'和'TableAndColumn'和'TableAndColumnAndRow'。这将非常快速地失控。我同意lamdba方法的可读性稍差,但它在写作时会给你带来好处,你知道你正在使用'IColumn',而不是'TableAndColumn'(这不太清楚你可以做什么)。特别是如果一列有与表相同的方法,即'Name()' – Rob