《模式——工程化实现及扩展》(设计模式C# 版)《连贯接口 Fluent Interface》——“自我检验"参考答案

转自:《模式——工程化实现及扩展》(设计模式C# 版)

http://www.cnblogs.com/callwangxiang/

 

 

http://www.cnblogs.com/callwangxiang/archive/2011/05/31/ExerciseAAFluentInterface.html的参考答案

 

 

参考答案

 

设计要点:

  1. 采用连贯接口设计表格的创建过程
  2. 由于表格Head涉及一层嵌套、Body涉及两层嵌套,因此为了便于调整和修改,每个节点元素类型都要保留回溯到父节点的引用

 

1、设计具有Fluent特征的抽象节点类型

/// <summary>
/// 修改后具有Fluent特征的集合类型
/// </summary>
/// <typeparam name="T">集合元素类型</typeparam>
/// <typeparam name="TParent">父节点类型</typeparam>
class FluentCollection<TElement, TParent>
    where TElement : class 
    where TParent : class 
{
    protected List<TElement> list = new List<TElement>();
    TParent parent;

    public FluentCollection(TParent parent)
    {
        if(parent == null) throw new ArgumentNullException("parent");
        this.parent = parent;
    }

    /// <summary>
    /// 返回父节点
    /// </summary>
    public TParent Parent{get{ return parent;}}

    /// <summary>
    /// 如何获得一个TElement类型实例的委托
    /// </summary>
    public Func<TElement> GetInstance { get; set; }

    /// <summary>
    /// 具有fluent特征的追加操作
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    public FluentCollection<TElement, TParent> Add(TElement t)
    {
        list.Add(t);
        return this;
    }

    /// <summary>
    /// 具有fluent特征的空置操作
    /// </summary>
    /// <returns></returns>
    public FluentCollection<TElement, TParent> Skip
    {
        get
        {
            list.Add(GetInstance());
            return this;
        }
    }

    /// <summary>
    /// 执行LINQ的foreach操作
    /// </summary>
    /// <param name="action"></param>
    public void ForEach(Action<TElement> action)
    {
        list.ForEach(action);
    }
}

/// <summary>
/// 父节点为table的元素
/// </summary>
class WithTableObject
{
    Table table;    //  父节点
    public WithTableObject(Table table)
    {
        if(table == null) throw new ArgumentNullException("table");
        this.table = table;
    }

    /// <summary>
    /// 指向父节点——table
    /// </summary>
    public Table Parent{get{ return table;}}
}

 

2、定义各个节点类型

class Notation
{
    public Notation(){Data = string.Empty;}
    public Notation(string data) {Data = data; }
    public string Data { get; private set; }
}

/// <summary>
/// n元素
/// </summary>
class Item : Notation
{
    public Item():base(){}
    public Item(string data) : base(data){}
}

/// <summary>
/// col 元素
/// </summary>
class Column : Notation
{
    public Column():base(){}
    public Column(string data) : base(data) { }
}

/// <summary>
/// line 元素 
/// </summary>
class Line
{
    FluentCollection<Item, Line> items;
    Body body;

    public Line(Body body)
    {
        if(body == null) throw new ArgumentNullException("body");
        this.body = body;
        items = new FluentCollection<Item, Line>(this)
                    {
                        GetInstance = () => { return new Item(); }
                    };
    }

    /// <summary>
    /// 父节点
    /// </summary>
    public Body Body { get { return body; } }
        
    public FluentCollection<Item, Line> Items { get { return items; } }

    public Line NewLine{get{return body.NewLine;}}
}


/// <summary>
/// body 元素
/// </summary>
class Body : WithTableObject
{
    List<Line> lines = new List<Line>();
    public Body(Table table) : base(table){}

    public Line NewLine
    {
        get
        {
            var line = new Line(this);
            lines.Add(line);
            return line;
        }
    }

    public List<Line> Lines { get { return lines;}}
} 

/// <summary>
/// head 元素
/// </summary>
class Head : WithTableObject
{
    FluentCollection<Column, Head> columns;

    public Head(Table table) : base(table)
    {
        columns = new FluentCollection<Column, Head>(this)
                        {
                            GetInstance = () => { return new Column(); }
                        };
    }
        
    public FluentCollection<Column, Head> Columns { get { return columns; } }
}

    
class Table
{
    string name;
    Body body;
    Head head;

    public Table()
    {
        body = new Body(this);
        head = new Head(this);
    }

    public Table Name(string name)
    {
        if(string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
        this.name = name;
        return this;
    }

    public override string ToString(){return name;}

    public Body Body{get{ return body;}}
    public Head Head{get{ return head;}}
}

 

3、定义生成电子表格的数据类型

class Notation
{
    public Notation(){Data = string.Empty;}
    public Notation(string data) {Data = data; }
    public string Data { get; private set; }
}

/// <summary>
/// n元素
/// </summary>
class Item : Notation
{
    public Item():base(){}
    public Item(string data) : base(data){}
}

/// <summary>
/// col 元素
/// </summary>
class Column : Notation
{
    public Column():base(){}
    public Column(string data) : base(data) { }
}

/// <summary>
/// line 元素 
/// </summary>
class Line
{
    FluentCollection<Item, Line> items;
    Body body;

    public Line(Body body)
    {
        if(body == null) throw new ArgumentNullException("body");
        this.body = body;
        items = new FluentCollection<Item, Line>(this)
                    {
                        GetInstance = () => { return new Item(); }
                    };
    }

    /// <summary>
    /// 父节点
    /// </summary>
    public Body Body { get { return body; } }
        
    public FluentCollection<Item, Line> Items { get { return items; } }

    public Line NewLine{get{return body.NewLine;}}
}


/// <summary>
/// body 元素
/// </summary>
class Body : WithTableObject
{
    List<Line> lines = new List<Line>();
    public Body(Table table) : base(table){}

    public Line NewLine
    {
        get
        {
            var line = new Line(this);
            lines.Add(line);
            return line;
        }
    }

    public List<Line> Lines { get { return lines;}}
} 

/// <summary>
/// head 元素
/// </summary>
class Head : WithTableObject
{
    FluentCollection<Column, Head> columns;

    public Head(Table table) : base(table)
    {
        columns = new FluentCollection<Column, Head>(this)
                        {
                            GetInstance = () => { return new Column(); }
                        };
    }
        
    public FluentCollection<Column, Head> Columns { get { return columns; } }
}

    
class Table
{
    string name;
    Body body;
    Head head;

    public Table()
    {
        body = new Body(this);
        head = new Head(this);
    }

    public Table Name(string name)
    {
        if(string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
        this.name = name;
        return this;
    }

    public override string ToString(){return name;}

    public Body Body{get{ return body;}}
    public Head Head{get{ return head;}}
}

 

 

4、单元测试

[TestClass]
public class FluentInterfaceFixture
{
    TableWriter writer;

    [TestInitialize]  
    public void Initialize()
    {
        writer = new TableWriter();
    }

    [TestMethod]
    public void TestFullFillTable()
    {
        writer.Output(
            new Table()
                .Name("full fill")
                .Head
                    .Columns
                        .Add(new Column("first"))
                        .Add(new Column("second"))
                        .Add(new Column("thrid"))
                    .Parent
                .Parent
                .Body
                    .NewLine.Items.Add(new Item("11")).Add(new Item("12")).Add(new Item("13")).Parent
                    .NewLine.Items.Add(new Item("21")).Add(new Item("22")).Add(new Item("23")).Parent
                .Body
            .Parent
            );
    }

    [TestMethod]
    public void TestSkipColumnTable()
    {
        writer.Output(
            new Table()
                .Name("skip columns")
                .Head
                    .Columns
                        .Add(new Column("first"))
                        .Skip
                        .Add(new Column("thrid"))
                    .Parent
                .Parent
                .Body
                    .NewLine.Items.Add(new Item("11")).Add(new Item("12")).Add(new Item("13")).Parent
                    .NewLine.Items.Add(new Item("21")).Add(new Item("22")).Add(new Item("23")).Parent
                .Body
            .Parent
            );
    }

    [TestMethod]
    public void TestSkiItemsTable()
    {
        writer.Output(
            new Table()
                .Name("skip items")
                .Head
                    .Columns
                        .Add(new Column("first"))
                        .Add(new Column("second"))
                        .Add(new Column("thrid"))
                    .Parent
                .Parent
                .Body
                    .NewLine.Items.Add(new Item("11")).Skip.Add(new Item("13")).Parent
                    .NewLine.Items.Add(new Item("21")).Add(new Item("22")).Skip.Parent
                .Body
            .Parent
            );
    }


    [TestMethod]
    public void TestSkipColumnsAndItemsTable()
    {
        writer.Output(
            new Table()
                .Name("skip columns and items")
                .Head
                    .Columns
                        .Add(new Column("first"))
                        .Skip
                        .Add(new Column("thrid"))
                    .Parent
                .Parent
                .Body
                    .NewLine.Items.Add(new Item("11")).Skip.Add(new Item("13")).Parent
                    .NewLine.Items.Add(new Item("21")).Add(new Item("22")).Skip.Parent
                .Body
            .Parent
            );
    }
}

 

5、测试结果

------ Test started: Assembly: Concept.Tests.dll ------

<table>
    <name>full fill</name>
    <head>
<col>first</col><col>second</col><col>thrid</col>    
    </head>
    <body>
        <line>
        <item>
<n>11</n><n>12</n><n>13</n>            
        </item>
        <item>
<n>21</n><n>22</n><n>23</n>            
        </item>
    </line>
    </body>
</table>

<table>
    <name>skip columns</name>
    <head>
<col>first</col><col></col><col>thrid</col>    
    </head>
    <body>
        <line>
        <item>
<n>11</n><n>12</n><n>13</n>            
        </item>
        <item>
<n>21</n><n>22</n><n>23</n>            
        </item>
    </line>
    </body>
</table>

<table>
    <name>skip items</name>
    <head>
<col>first</col><col>second</col><col>thrid</col>    
    </head>
    <body>
        <line>
        <item>
<n>11</n><n></n><n>13</n>            
        </item>
        <item>
<n>21</n><n>22</n><n></n>            
        </item>
    </line>
    </body>
</table>

<table>
    <name>skip columns and items</name>
    <head>
<col>first</col><col></col><col>thrid</col>    
    </head>
    <body>
        <line>
        <item>
<n>11</n><n></n><n>13</n>            
        </item>
        <item>
<n>21</n><n>22</n><n></n>            
        </item>
    </line>
    </body>
</table>

4 passed, 0 failed, 0 skipped, took 1.29 seconds (MSTest 10.0).

 

测试确认Fluent接口的有效性

posted @ 2011-05-31 23:27 蜡笔小王 阅读(...) 评论(...) 编辑 收藏