《模式——工程化实现及扩展》(设计模式C# 版)《连贯接口 Fluent Interface》——“自我检验"参考答案
转自:《模式——工程化实现及扩展》(设计模式C# 版)
http://www.cnblogs.com/callwangxiang/
http://www.cnblogs.com/callwangxiang/archive/2011/05/31/ExerciseAAFluentInterface.html的参考答案
参考答案
设计要点:
- 采用连贯接口设计表格的创建过程
- 由于表格Head涉及一层嵌套、Body涉及两层嵌套,因此为了便于调整和修改,每个节点元素类型都要保留回溯到父节点的引用
1、设计具有Fluent特征的抽象节点类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | /// <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> <font style= "background-color: rgba(255, 255, 0, 1)" > public TParent Parent{ get { return parent;}}</font> /// <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); <font style= "background-color: rgba(255, 255, 0, 1)" > return this ; </font> } /// <summary> /// 具有fluent特征的空置操作 /// </summary> /// <returns></returns> public FluentCollection<TElement, TParent> Skip { get { list.Add(GetInstance()); <font style= "background-color: rgba(255, 255, 0, 1)" > return this ;</font> } } /// <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> <font style= "background-color: rgba(255, 255, 0, 1)" > public Table Parent{ get { return table;}} </font>} |
2、定义各个节点类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 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> <font style= "background-color: rgba(255, 255, 0, 1)" > public Body Body { get { return body; } }</font> <font style= "background-color: rgba(255, 255, 0, 1)" > public FluentCollection<Item, Line> Items { get { return items; } }</font> <font style= "background-color: rgba(255, 255, 0, 1)" > public Line NewLine{ get { return body.NewLine;}}</font> } /// <summary> /// body 元素 /// </summary> class Body : WithTableObject { List<Line> lines = new List<Line>(); public Body(Table table) : base (table){} <font style= "background-color: rgba(255, 255, 0, 1)" > public Line NewLine </font> { get { var line = new Line( this ); lines.Add(line); return line; } } <font style= "background-color: rgba(255, 255, 0, 1)" > public List<Line> Lines { get { return lines;}} </font>} /// <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(); } }; } <font style= "background-color: rgba(255, 255, 0, 1)" > public FluentCollection<Column, Head> Columns { get { return columns; } }</font> } class Table { string name; Body body; Head head; public Table() { body = new Body( this ); head = new Head( this ); } <font style= "background-color: rgba(255, 255, 0, 1)" > public Table Name( string name)</font> { if ( string .IsNullOrEmpty(name)) throw new ArgumentNullException( "name" ); this .name = name; return this ; } public override string ToString(){ return name;} <font style= "background-color: rgba(255, 255, 0, 1)" > public Body Body{ get { return body;}} public Head Head{ get { return head;}}</font> } |
3、定义生成电子表格的数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 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、单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | [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、测试结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | ------ 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接口的有效性
贸易电子化,技术全球化
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?