在 Newbe.ObjectVistor 0.3 版本中我们非常兴奋的引入了一个紧张刺激的新特性:使用状态图来生成任意给定的 FluentAPI 设计。

开篇摘要

在非常多优秀的框架中都存在一部分 FluentAPI 的设计。这种 API 设计更加符合人类自言语言描述。使得代码更加具备可读性。

在 Newbe.ObjectVistor 0.3 版本中,我们设计引入了一种使用状态图来自动生成 FluentAPI 代码的机制。极大了简化了 FluentAPI 实现所需要的脑力劳动。

本篇我们将通过一些示例,来了解一下当前版本中该特性的主要效果。

整数累加 FluentAPI

假如,我们现在需要实现下面这样效果的一个 API:

[Test]
public void SumList()
{var sumBuilder = new SumBuilder(new List<int>());var re = sumBuilder.AddNumber(1).AddNumber(2).AddNumber(3).Sum();re.Should().Be(6);
}

这个 API 使用 FluentAPI 的方式来表述一个累加的过程。

为了实现这个 API 设计,在 Newbe.ObjectVisitor 0.3 中,使用下面这样一个状态图标记表述这个 API 设计:

stateDiagram[*]  --> AddNumber : AddNumber(int number)AddNumber  --> AddNumber : AddNumber(int number)AddNumber --> [*] : Sum() return int

这实际上是 mermaid 状态图标记。转换为图形即为下面这个效果。不需要过多的解释就可以理解:

SumBuilder

有了这个状态图之后,使用 Newbe.ObjectVisitor 中的 FluentApiDesignParserFluentApiFileGenerator 便可以生成如下代码。

using System;
using System.Collections.Generic;
using System.Linq;namespace Newbe.ObjectVisitor.Tests.SumBuilderFluentApi
{public class SumBuilder : Newbe.ObjectVisitor.IFluentApi, SumBuilder.ISumBuilder_AddNumber{private readonly List<int> _context;public SumBuilder(List<int> context){_context = context;}#region UserImplprivate void Core_AddNumber(int number){throw new NotImplementedException();}private int Core_Sum(){throw new NotImplementedException();}#endregion#region AutoGenerate
/// 此处省略了自动生成的固定代码部分,请到仓库中查看#endregion}
}

有了这个模板之后,只要实现 Core_AddNumberCore_Sum,一个符合预期设计的 FluentAPI 就完成了!

累加后累乘

现在,我们稍微改变一下需求。上节我们实现的是一个 1+2+3 这样的累加效果。现在我们需要一个 (1+2+3)*(4+5+6)*(7+8+9+10) 这样的效果。

示例的调用代码如下:

[Test]
public void MultipleSumList()
{var builder = new MultipleSumBuilder(new List<List<int>>());var re = builder.AddNumber(1).AddNumber(2).NextFactor().AddNumber(3).Sum();re.Should().Be(9);
}

为了实现这个效果,我们修改一下状态图,增加一条新的规则,得到:

stateDiagram[*]  --> AddNumber : AddNumber(int number)AddNumber  --> AddNumber : AddNumber(int number)AddNumber  --> AddNumber : NextFactor()AddNumber --> [*] : Sum() return int

如图:

MultipleSumBuilder

创建数据库链接字符串

前面的示例或许缺乏生产实际,现在添加一个生产示例。我们现在要实现一个 ConnectionStringBuilder 用来创建数据库连接字符串,其中有以下限制:

  1. 必须指定 Host。

  2. 身份认证方式必须且只能指定一种,要么是用户名密码方式,要么是 Windows 凭据。

首先,我们有一个模型来保存上面提到的数据。

public class ConnectionStringModel
{public string Host { get; set; }public string Username { get; set; }public string Password { get; set; }public bool? IsWindowsAuthentication { get; set; }
}

接着,我们直接使用状态图来设计这个 FluentAPI。设计结果如下:

stateDiagram[*]  --> SetHost : SetHost(string host)SetHost  --> UseUsernamePassword : UseUsernamePassword(string username, string password)SetHost  --> UseWindowsAuthentication : UseWindowsAuthentication()UseUsernamePassword --> [*] : Build() return stringUseWindowsAuthentication --> [*] : Build() return string

如图:

ConnectionStringBuilder

有了设计,接下来就是使用生成器啪嗒一下生成代码,然后添加实现,这里只展示需要自己实现的内容:

#region UserImplprivate void Core_SetHost(string host)
{_context.Host = host;
}private void Core_UseUsernamePassword(string username, string password)
{_context.Username = username;_context.Password = password;
}private void Core_UseWindowsAuthentication()
{_context.IsWindowsAuthentication = true;
}// 这里使用 ObjectVisitor 将一个模型的非空字段拼接在一起
private static readonly ICachedObjectVisitor<ConnectionStringModel, StringBuilder> Builder =default(ConnectionStringModel)!.V().WithExtendObject<ConnectionStringModel, StringBuilder>().ForEach((name, value, sb) => Append(name, value, sb)).Cache();private static void Append(string name, object? value, StringBuilder sb)
{if (value != null){sb.Append($"{name}={value};");}
}private string Core_Build()
{var sb = new StringBuilder();Builder.Run(_context, sb);return sb.ToString();
}#endregion

下面是简单的两个测试用例:

public class ConnectionStringBuilderTest
{[Test]public void UseUsernamePassword(){var builder = new ConnectionStringBuilder(new ConnectionStringModel());var re = builder.SetHost("localhost").UseUsernamePassword("yueluo", "dalao").Build();re.Should().Be("Host=localhost;Username=yueluo;Password=dalao;");}[Test]public void UseWindowsAuthentication(){var builder = new ConnectionStringBuilder(new ConnectionStringModel());var re = builder.SetHost("localhost").UseWindowsAuthentication().Build();re.Should().Be("Host=localhost;IsWindowsAuthentication=True;");}
}

值得特别提出但是,这和直接使用 ConnectionStringModel 模型来构建字符串,通过 FluentAPI 的形式,约束了开发者能够赋值的属性。可以避免忘记对必要的属性赋值或者错误赋值等等出错情况。

Get 和 Delete 没有 Body,Post 和 Put 才有

和上一节类型,我们使用 FluentAPI 来构建请求,但是需要满足以下约束:

  1. 可以指定 Uri

  2. Get 和 Delete 不能指定 Body,但是 Post 和 Put 可以

上设计:

stateDiagram[*]  --> Get : Get()Get --> GetUri : SetUri(Uri uri) share _SetUriCore[*]  --> Delete : Delete()Delete --> DeleteUri : SetUri(Uri uri) share _SetUriCore[*]  --> Post : Post()Post --> PostUri : SetUri(Uri uri) share _SetUriCorePostUri --> SetContent : _SetContent share _SetContentCore[*]  --> Put : Put()Put --> PutUri : SetUri(Uri uri) share _SetUriCorePutUri --> SetContent : _SetContent share _SetContentCoreSetContent --> [*] : _Build return HttpRequestMessageGetUri --> [*] : _Build return HttpRequestMessageDeleteUri --> [*] : _Build return HttpRequestMessage

上图:

RequestBuilder

注意,这里引入了一些奇怪的关键词 share ,由于这些关键词还未全部定稿,因此不展开说明。

可以通过以下链接,查看生成的代码和测试用例。

https://github.com/newbe36524/Newbe.ObjectVisitor/tree/main/src/Newbe.ObjectVisitor/Newbe.ObjectVisitor.Tests/HttpClientFluentApi

https://gitee.com/yks/Newbe.ObjectVisitor/tree/main/src/Newbe.ObjectVisitor/Newbe.ObjectVisitor.Tests/HttpClientFluentApi

造一辆汽车一定要四个轮子一个引擎

我们需要实现一个 CarBuilder,有一些约束:

  1. CarBuilder 当且仅当在调用四次 AddWheel 和一次 AddEngine 之后才能出现 Build 方法

  2. 虽然限制了次数,但是,顺序不能限定,什么顺序都可以。

上设计:

stateDiagram[*]  --> W1 : AddWheel(int size) share AddWheelW1 --> W2 : AddWheel(int size) share AddWheelW2 --> W3 : AddWheel(int size) share AddWheelW3 --> W4 : AddWheel(int size) share AddWheel[*] --> E : AddEngine(string engine) share AddEngineE --> WE1 : AddWheel(int size) share AddWheelWE1 --> WE2 : AddWheel(int size) share AddWheelWE2 --> WE3 : AddWheel(int size) share AddWheelWE3 --> WE4 : AddWheel(int size) share AddWheelW1 --> WE1 : AddEngine(string engine) share AddEngineW2 --> WE2 : AddEngine(string engine) share AddEngineW3 --> WE3 : AddEngine(string engine) share AddEngineW4 --> WE4 : AddEngine(string engine) share AddEngineWE4 --> [*] : Build() return Car

上图,这个图从出发点出发,不论怎么走都会经过四次 AddWheel 和 一次 AddEngine:

CarBuilder

注意,虽然设计看起来非常复杂,但是,需要手写的代码只有非常简短的两段:

#region UserImplprivate void Shared_AddWheel(int size)
{if (_context.Wheel1 == 0){_context.Wheel1 = size;return;}if (_context.Wheel2 == 0){_context.Wheel2 = size;return;}if (_context.Wheel3 == 0){_context.Wheel3 = size;return;}if (_context.Wheel4 == 0){_context.Wheel4 = size;return;}
}private void Shared_AddEngine(string engine)
{_context.Engine = engine;
}private Car Core_Build()
{return _context;
}#endregion

可以通过以下链接,查看生成的代码和测试用例。

https://github.com/newbe36524/Newbe.ObjectVisitor/tree/main/src/Newbe.ObjectVisitor/Newbe.ObjectVisitor.Tests/CarBuilder

https://gitee.com/yks/Newbe.ObjectVisitor/tree/main/src/Newbe.ObjectVisitor/Newbe.ObjectVisitor.Tests/CarBuilder

本篇总结

这是一个很有意思的设计,如果你对这个设计很感兴趣,有新奇的想法,欢迎关注 Newbe.ObjectVisitor 项目,提出您的宝贵想法。

我画着图,FluentAPI 她自己就生成了相关推荐

  1. 潘石屹首次Python考试成绩 99 分,失分点:画完图后忘了隐藏画笔的箭头

    机器之心报道 参与:蛋酱 「人生苦短,我潘石屹考了 Python.」 5 月 16 日凌晨,房地产大佬.SOHO 中国董事长潘石屹在微博晒出了自己的第一张 Python 考试成绩单:99 分,优秀! ...

  2. python画柱状图 画折线图

    # 画柱状图     plt.bar(x_labels, grid) 画折线图     """     for i in range(len(grid)-1):     ...

  3. 用计算机画函数图像教案,信息技术应用 用计算机画函数图象教案1

    谭诗环 地区: 四川省 - 绵阳市 - 安 县 学校:安县黄土镇仁和学校 共1课时 信息技术应用 用计算机画函数图象">信息技术应用 用计算机- 初中数学       人教2011课标 ...

  4. matlab画三维图

    plot与surf与mesh三个是画三维图经常用到的函数,下面看看三者画出的图的区别: [x,y]=meshgrid(-2:0.1:2);     z=x.*exp(-x.^2-y.^2);     ...

  5. numpy.loadtxt画功率谱图

    (非专业相关可以选择性阅读) 从CAMB中可以得到.dat文件,由于第一行有L,TT,EE等字符使得numpy无法正常读取文件:故可以手动将第一行去掉,剩下的以python写代码画图,如下: impo ...

  6. python用matplotlib画人口图_Python+Matplotlib画contour图

    - 全文阅读3分钟 - 在本文中,你将学习到以下内容: 理解画contour图的数据结构 contour图的参数调节 如何添加colorbar 我们先假设x, y的取值范围如下: import mat ...

  7. python怎么做折线图_python怎么画折线图

    匿名用户 1级 2018-01-18 回答 一.环境准备 linux ubuntu 下需安装下面三个包: Numpy, Scipy,Matplotlib 分别输入下面的代码进行安装: [plain]  ...

  8. 从PCB焊接角度谈画PCB图时应注意的问题

    转载自 https://mp.weixin.qq.com/s?__biz=MzAxNTIzOTcyMw==&mid=2650183571&idx=1&sn=aea06ed898 ...

  9. 用计算机绘制函数图象教案,信息技术应用 用计算机画函数图象教学设计(教案)...

    庞丹 地区: 四川省 - 广元市 - 昭化区 学校:广元民盟烛光初级中学 共1课时 信息技术应用 用计算机画函数图象">信息技术应用 用计算机- 初中数学       人教2011课标 ...

  10. python画折线图代码-用Python画论文折线图、曲线图?几个代码模板轻松搞定!

    前言 这几天在搞论文图,唉说实话抠图这种东西真能逼死人.坐在电脑前抠上一天越看越丑,最后把自己丑哭了-- 到了画折线图分析的时候,在想用哪些工具的时候.首先否决了excel,读书人的事,怎么能用exc ...

最新文章

  1. Android studio Dialog 弹出式对话框
  2. python实现后台系统的JWT认证
  3. 转化百分比_小秘诀教你如何快速提升大众点评访客转化率!
  4. c语言键盘连续双击程序,编了个C语言的键盘程序有点问题,请指点下!
  5. Java多线程(七)之同步器基础:AQS框架深入分析
  6. marked override, but does not override
  7. Junit运行报initializationError错误
  8. 在python中获取当前工作目录可以通过_python-获取当前工作路径
  9. windows无法访问指定设备_恢复 你的电脑/设备需要修改 未连接或无法访问所需设备。...
  10. arcgis图层控制
  11. 使用 Flink Hudi 构建流式数据湖平台
  12. 算法48---原子的数量【栈】
  13. N1网络命令-ping
  14. C++basic_string(字符串类模板)
  15. 计算机信息技术教程(笔记)
  16. B站三季度财报解读丨从0到2.67亿月活,B站生态亮眼的密钥何在?
  17. 气体涡轮流量计和涡街流量计的区别
  18. 运维数据防泄露解决方案
  19. autojs脚本通用ui模板解决了一些已知问题
  20. 独家丨支付宝在愚人节推出全球首款会飞的区块链手机!

热门文章

  1. 使用ASP.NET广告控件的XML语言创建广告链接--ASP.NET
  2. .NET平台下几种SOCKET模型的简要性能供参考
  3. jQuery.extend与jQuery.fn.extend的区别分析与实例
  4. php 跨区域,如何构造PHP的内容包括在非安全(http://)和安全(https://)区域以及跨多个目录使用?...
  5. canpro脚本_AE/PR脚本-创建编辑导入导出专业字幕脚本 Subtitle Pro 2.8.0 + 使用教程...
  6. IE不能直接顯示PDF的原因分析和解決方法
  7. 初学Hibernate
  8. [Todo] 乐观悲观锁,自旋互斥锁等等
  9. iOS 集合的深复制与浅复制
  10. Linux SSH Publickey登录