《模式——工程化实现及扩展》(设计模式C# 版)《连贯接口 Fluent Interface》——“自我检验"参考答案
转自:《模式——工程化实现及扩展》(设计模式C# 版)
http://www.cnblogs.com/callwangxiang/
http://www.cnblogs.com/callwangxiang/archive/2011/05/31/ExerciseAAFluentInterface.html的参考答案
参考答案
设计要点:
- 采用连贯接口设计表格的创建过程
- 由于表格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接口的有效性
贸易电子化,技术全球化