这个流畅的界面可以简化吗?

时间:2015-12-15 07:15:08

标签: c# generics fluent-interface

我正在尝试实现一个流畅的界面,让我指定一个包含列的表,其中表和列具有属性。我希望能够像这样初始化表类:

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包含对Table实例和Column实例的引用。接口公开了ITableIColumn的选定方法,并调用适当的实例。

这种方法是否复杂?扩展方法是否会以任何方式解决这个问题?

1 个答案:

答案 0 :(得分: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;
    }
}