让它在Java中流动

正如我在介绍Flow Design的上一篇文章中所宣布的那样,我想展示一个Java的具体实现。 该示例的代码可以在GitHub找到

我将关注Ralf Westphal的博客文章“ IODA Architecture for Example ”,该文章针对以下场景进行了流程设计和实现:

生成一个应用程序,将用户输入的罗马数字转换为阿拉伯数字,反之亦然。

阅读他的文章以获取更多详细信息可能也很有价值。 但是,我将重复他的步骤(有时会缩短一些步骤),以使您了解整个情况。 几乎所有图纸都来自Ralf的博客。

需求分析

分析方案说明后,必须有一个用户可以键入罗马或阿拉伯数字的应用程序。由于不需要特别说明,因此简单的控制台程序可以满足此要求。

可能看起来像:

> convertroman XIV
14
> convertroman 42
XLII
>

关于验证没有任何说明。 因此,验证将是非常基本的验证。

解决方案设计

当然,我们这里不做面向对象的设计。 我们还有另一个意图。 流程设计是目标。 因此,我们从顶层抽象开始。 从需求分析中我们所知道的就是,“世界”是由用户触发的:用户启动程序。 在流程设计中,可以表示为:

这是最高级别的抽象。

流程设计

现在,“零件”(又称功能单元)开始发挥作用。 下一个设计步骤是弄清楚,哪些处理步骤在最高抽象级别上构建根部分。 功能单元“罗马转换”成为集成层次结构的基础。

现在重复此过程,直到确实需要将功能单元划分为更多,更小的处理步骤,并且它们变得清晰且易于实现。

对于上图中的功能单元,两个外部的功能单元是相当了解的。 从命令行读取输入并在其上显示字符结果很容易实现。 但是,功能单元“转换”不是很清楚。 需要进一步完善。

直到现在,“转换”还是一项操作。 但是现在它已经开放,成为集成单元,可以连接更精细的操作。 验证在这里也应该有它的位置。 让我们放在一起:

Java流程

“从罗马转换”和“转换为罗马”这两个功能单元均包含此实现的核心域逻辑。 这里需要更多的思考来进行进一步分解。 原始文章将其示例为“从罗马转换”部分,因为与“转换为罗马”相比这并不明显。 我在这里重复一遍:

  • 罗马数字的每个罗马数字都转换为它的值。 结果是一个整数列表。 例如,

    XIV”-> [10,1,5]

  • 如果列表中的较小值先于较小的值出现,则取反较小的值。 例如[10,1,5]-> [10,-1,5]。 在原始文章中称为“应用减法规则”。

  • 累积这些值以计算它们的总和。 例如[10,-1,5]-> 14

将这三个步骤联系在一起,就形成了以下“域过程”:

Java流程

现在,无需进一步完善。 显然,必须执行特定的操作。 仅错误情况已被排除在外。 他们来了:

Java流程

我想,现在很清楚了,为什么将此设计方法称为Flow Design 。 这是关于设计应用程序的数据流。 它由连接起来并由数据类型标记的操作和集成单元组成,它们从一个单元流向另一个单元。 因此,现在只剩下流动数据的设计了。 但是首先, 流程设计的整个结果以红色标记。

Java流程

资料设计

IODA体系结构的[I]集成和[O]操作实体已设置。 要使用的[A] PI确实不值得一提; 显然是Java语言框架。 但是,仍然需要设计[D] ata。

数据类型已经在流程设计图中,并且可能会被读出。 在这里,它们被提取:

Java流程

从功能单元到数据类型的连接的末尾有一个实心圆。 这就是在Flow Design中标记依赖项的方式。 因此,依赖性在此并不超出范围。 但是,它们仅限于自然的情况

  • 功能单元取决于它们接收或发送的数据类型; 和
  • 功能单元可能取决于保持状态的单元(例如数据库访问权限)

我们的设计中没有状态。 但是数据类型在那里。 对于那些人来说,面向对象的设计如果不是琐碎的话,将是一个合适的工具。 但是,遵循KISS原则 ,我们不会为域名概念罗马数字阿拉伯数字引入特定类型。 第一个只是字符串,第二个是整数。 如果也可以使用普通的Java类型来设计特定类型,那么就没有任何价值。 值列表很容易用整数数组或Java列表表示。

无论如何,此处做出的关于数据类型的设计决定不会改变基本架构。 操作单元处理数据。 因此,如果数据设计发生变化,则它们主要受此影响。

类设计

当我们要用Java实现设置设计时,我们需要考虑类,因为类是面向对象语言的主要代码构建块之一。 代码构建块在模块的设计维度上形成层次结构,从方法或功能作为最小的代码模块开始,然后是类,包,库,组件和微服务。

与面向对象设计的区别很明显在这里出现:我们不首先考虑类。 无需提出“候选课程”。 我们首先考虑行为/功能。 这些类是直接从流程设计中看到的模式派生的。 气泡分为以下几类:

Java流程

并非所有这些类都是显而易见的。 拉尔夫·韦斯特法尔(Ralf Westphal)解释他的分组决定如下:

  • 提供者 :在任何软件中,使用API​​都是一个“硬”方面。 应将其隔离到特定模块中。 通常,每个API都应使用自己的类进行封装-但是在这种情况下,只有很少的一个类可以完成。
  • FromRomanConversionToRomanConversion :进行实际转换是其自身的一个方面。 由于存在两个转换方向,因此每个模块似乎都是按顺序排列的。
  • RomanConversion :此类属于应用程序的粗粒度域。 它包含与转换无关的“辅助功能”。
  • 主体 :此类表示整体功能-但无需与用户交互。 就像程序应该执行的内部API一样。
  • 头部 :头部负责触发身体行为。 它将人体与用户输入和输出的信息整合在一起。

类图遵循IODA架构; 集成,操作和API是可识别的。 由于场景非常简单,因此没有数据类型被设计,因此仅缺少数据:

接口和静态方法

在课堂设计上还有更多决定权。 特别是,我们需要知道哪些类将被实例化,以及哪一个仅成为静态方法的代码容器。

由于不涉及任何状态, 因此不需要实例化RomanConversionFromRomanConversionToRomanConversion 。 它们包含的方法可以是静态的。

对于提供者,它看起来有所不同。 由于将要实现API访问,因此在这里应用了接口隔离原理 。 因此, Head也需要实例化。

另外,将“ 主体”描述为允许隔离测试的接口。

包装设计

代码模块层次结构中的下一层是Java包。 这是非常特定于Java的,原始设计文章没有它,因为C#的名称空间的语言概念并不像Java包那样强大的代码结构化工具。 Java的程序包定义了类和方法的可见性,因此功能更强大。

从结构上讲,我的包装设计与原始文章的库设计非常相似。

Java流程

  • 转换包含业务领域的所有类别
  • 提供者具有处理API的所有类
  • 是顶级集成商的所在地
  • 主体包含负责“后端”集成的类; 在这里,行为被创建

但是,我介绍了另一个分组级别。 我将所有包含操作类的包放在子包操作下 ,所有要集成到子包集成中的类 。 最后,我还有打包合同 。 它包含所有设计的接口。

Java流程

所有软件包的名称空间前缀de.grammarcraft.javaflow.examples.convertroman都具有唯一性。

图书馆设计

将代码组织到库中是构建模块层次结构的下一个工具。 在Java中,通常将其映射到JAR归档文件。

在设计过程中,这是非常值得考虑的,因为它通常是最小的部署工件。 在此级别上,可以将功能扩展和错误修复应用于已经推出的解决方案,以通过替换库来确保该解决方案的可扩展性。 特别是在分布式环境中,库是形成分布式模块格局的代码的切入点。

由于此解决方案非常简单,因此将只有一个库,一个JAR文件。

元件设计

Ralf Westphal将术语“组件”定义为一个或多个库的集合,由特定于平台的合同描述。 可以将组件想像成具有门面作为入口点的JAR文件束,或者像OSGi束。

将代码结构化为组件,其边界由诸如界面或外观类之类的契约确定,这是专业软件开发的前提。 至少,它对并行开发的团队有很大帮助。 每个团队成员都在其组件合同的标题下开发一个组件,同时应用其他可能尚未准备好的外国组件合同。

实作

实现是直接完成的。 这是Eclipse中产生的项目结构:

Java流

您可以在Github上查看源项目。

让我们仔细看看如何在Java中实现拆分流。 现在,我们可以利用lambda的新Java语言功能,这使实现变得非常简洁。 由于Java不允许设置内部DSL,因此至少要尽可能简洁(目前在Java中)。

Java流

查看上面的代码,您可能需要花一点时间来了解发生了什么。 但是,这只是一个简单的实现。

让我们采取最高流量。

Java流

这将导致下面已经显示的源代码:

Java流

只需从上到下以及从左到右读取函数调用即可。 缩进是流程中的不同路径。

实际上,图中的每个气泡都转换为一个方法调用。 如果气泡有一个输出,该函数将简单地返回它。 如果气泡有一个以上的输出,则将它们实现为延续–在Java中以数据类型作为通用参数将其定义为Consumer的函数对象。 因此,实现变得类型安全。

这些延续使代码难以阅读。 但仅在开始时。 这很不寻常,没有错。 您已经受了很长时间的培训,可以从右到左和由内而外读取嵌套的函数调用; 但是一旦习惯了继续学习,您就会意识到阅读代码又变得多么容易。

拉尔夫·韦斯特法尔

结论

我已经证明,基于流程设计的Java IODA架构实现就像Ralf Westphal在他的C#文章中所展示的一样容易。 实际上,这并不奇怪,因为使用lambda语言概念时,Java的委托人和lambda表示法与C#具有相似的强大功能。

但是,阅读Java或C#实现中的流程仍然有些困难。 如果可以直接在集成单元中指定流程,如下所示:

Java流程

您可能不会更接近故意流程设计图。 那会大大提高可读性,不是吗?

如果您使用允许内部DSL实现的编程语言,那么即使在JVM上,也可以使用这种表示法。 我将在下一篇文章中展示如何在Scala和Xtend中完成此操作。 我将实现转换罗马数字的相同示例,以使其具有更好的可比性。

如果有人已经想看看,它已经在以下GitHub项目中实现:

敬请关注。

这篇文章最初发表在Beyond Coding上

翻译自: https://jaxenter.com/let-flow-java-139880.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值