博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
我画着图,FluentAPI 她自己就生成了
阅读量:4032 次
发布时间:2019-05-24

本文共 7726 字,大约阅读时间需要 25 分钟。

在 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
());    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
 _context;        public SumBuilder(List
 context)        {            _context = context;        }        #region UserImpl        private 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
>());    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 string    UseWindowsAuthentication --> [*] : 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
 Builder =    default(ConnectionStringModel)!.V()        .WithExtendObject
()        .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 _SetUriCore    PostUri --> SetContent : _SetContent share _SetContentCore    [*]  --> Put : Put()    Put --> PutUri : SetUri(Uri uri) share _SetUriCore    PutUri --> SetContent : _SetContent share _SetContentCore    SetContent --> [*] : _Build return HttpRequestMessage    GetUri --> [*] : _Build return HttpRequestMessage    DeleteUri --> [*] : _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 AddWheel    W1 --> W2 : AddWheel(int size) share AddWheel    W2 --> W3 : AddWheel(int size) share AddWheel    W3 --> W4 : AddWheel(int size) share AddWheel    [*] --> E : AddEngine(string engine) share AddEngine    E --> WE1 : AddWheel(int size) share AddWheel    WE1 --> WE2 : AddWheel(int size) share AddWheel    WE2 --> WE3 : AddWheel(int size) share AddWheel    WE3 --> WE4 : AddWheel(int size) share AddWheel    W1 --> WE1 : AddEngine(string engine) share AddEngine    W2 --> WE2 : AddEngine(string engine) share AddEngine    W3 --> WE3 : AddEngine(string engine) share AddEngine    W4 --> WE4 : AddEngine(string engine) share AddEngine    WE4 --> [*] : 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 项目,提出您的宝贵想法。

转载地址:http://exkdi.baihongyu.com/

你可能感兴趣的文章
Linux常用统计命令之wc
查看>>
测试必会之 Linux 三剑客之 sed
查看>>
Socket请求XML客户端程序
查看>>
Java中数字转大写货币(支持到千亿)
查看>>
Java.nio
查看>>
函数模版类模版和偏特化泛化的总结
查看>>
VMware Workstation Pro虚拟机不可用解决方法
查看>>
最简单的使用redis自带程序实现c程序远程访问redis服务
查看>>
redis学习总结-- 内部数据 字符串 链表 字典 跳跃表
查看>>
iOS 对象序列化与反序列化
查看>>
iOS 序列化与反序列化(runtime) 01
查看>>
iOS AFN 3.0版本前后区别 01
查看>>
iOS ASI和AFN有什么区别
查看>>
iOS QQ侧滑菜单(高仿)
查看>>
iOS 扫一扫功能开发
查看>>
iOS app之间的跳转以及传参数
查看>>
iOS __block和__weak的区别
查看>>
Android(三)数据存储之XML解析技术
查看>>
Spring JTA应用之JOTM配置
查看>>
spring JdbcTemplate 的若干问题
查看>>