《模式——工程化實現及擴展》(設計模式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接口的有效性
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章