如何使用Fluent API创建更简单、更直观的代码
这篇文章给大家分享的是有关如何使用Fluent API创建更简单、更直观的代码的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
什么是 Fluent-API?
当我们在软件工程的上下文中谈论时,fluent-API 是一种面向对象的 API,其设计主要基于方法链。
这个概念由Eric Evans和Martin Fowler于 2005 年创建,旨在通过创建特定领域语言 ( DSL )来提高代码可读性。
在实践中,创建一个流畅的 API 意味着开发一个 API,其中不需要记住接下来的步骤或方法,允许一个自然连续的序列,就好像它是一个选项菜单。
这种自然的节奏与餐厅甚至快餐连锁店的工作方式类似,因为当您将一道菜放在一起时,选项会根据你所做的选择而有所不同。例如,如果你选择鸡肉三明治,则会根据所选菜肴等建议配菜。
Java 上下文中的 Fluent API
在 Java 世界中,我们可以想到此类实现的两个著名示例。
第一个是JOOQ框架,这是一个由Lukas Eder领导的项目,它促进了 Java 和关系数据库之间的通信。JOOQ 最显着的区别在于它是面向数据的,这有助于避免和/或减少与关系和面向对象相关的阻抗问题或损失。
Query query = create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) .from(BOOK) .join(AUTHOR) .on(BOOK.AUTHOR_ID.eq(AUTHOR.ID)) .where(BOOK.PUBLISHED_IN.eq(1948));String sql = query.getSQL();List另一个例子是在企业 Java 世界规范内的非关系数据库,即 NoSQL。其中包括Jakarta EE,它是同类中的第一个规范,并成为Eclipse Foundation旗下的Jakarta NoSQL。
本规范的目的是确保 Java 和 NoSQL 数据库之间的顺畅通信。
DocumentQuery query = select().from("Person").where(eq(Document.of("_id", id))).build();Optional person = documentTemplate.singleResult(query);System.out.println("Entity found: " + person); 一般来说,一个 fluent API 分为三个部分:
最终的对象或结果:总的来说,fluent-API 类似于构建器模式,但最强大的动态与 DSL 相结合。在这两种情况下,结果往往是代表流程或新实体结果的实例。
选项:在这种情况下,是将用作"我们的交互式菜单"的接口或类的集合。从一个动作来看,这个想法是按照直观的顺序只显示下一步可用的选项。
结果:在所有这个过程之后,答案可能会或可能不会导致实体、策略等的实例。关键点是结果必须是有效的。
流体 API 实践
为了演示这一概念,我们将创建一个三明治订单,其中包含具有相应购买价格的订单的预期结果。流程如下所示。
当然,有多种方法可以实现这种流畅的 API 功能,但我们选择了一个简短的版本。
正如我们已经提到的 API 的三个部分--对象、选项和结果--我们将从"订单"接口将表示的顺序开始。一个亮点是这个界面有一些界面,它们将负责展示我们的选项。
public interface Order { interface SizeOrder { StyleOrder size(Size size); } interface StyleOrder { StyleQuantityOrder vegan(); StyleQuantityOrder meat(); } interface StyleQuantityOrder extends DrinksOrder { DrinksOrder quantity(int quantity); } interface DrinksOrder { Checkout softDrink(int quantity); Checkout cocktail(int quantity); Checkout softDrink(); Checkout cocktail(); Checkout noBeveragesThanks(); } static SizeOrder bread(Bread bread) { Objects.requireNonNull(bread, "Bread is required o the order"); return new OrderFluent(bread); }这个 API 的结果将是我们的订单类。它将包含三明治、饮料及其各自的数量。
在我们返回教程之前的快速附加组件
我们不会在本文中关注但值得一提的一点与货币的表示有关。
当涉及到数值运算时,最好使用 BigDecimal。那是因为,根据Java Effective书籍和博客When Make a Type 之类的参考资料,我们了解到复杂类型需要唯一的类型。这种推理,再加上"不要重复自己"的实用主义,结果就是使用了 Java 货币规范:The Money API。
import javax.money.MonetaryAmount;import java.util.Optional;public class Checkout { private final Sandwich sandwich; private final int quantity; private final Drink drink; private final int drinkQuantity; private final MonetaryAmount total; //...}旅程的最后一步是 API 实现。它将负责代码的"丑陋"部分,使 API 看起来很漂亮。
由于我们不使用数据库或其他数据引用,因此价格表将直接放置在代码中,并且我们打算使示例尽可能简单。但值得强调的是,在自然环境中,这些信息会存在于数据库或服务中。
import javax.money.MonetaryAmount;import java.util.Objects;class OrderFluent implements Order.SizeOrder, Order.StyleOrder, Order.StyleQuantityOrder, Order.DrinksOrder { private final PricingTables pricingTables = PricingTables.INSTANCE; private final Bread bread; private Size size; private Sandwich sandwich; private int quantity; private Drink drink; private int drinkQuantity; OrderFluent(Bread bread) { this.bread = bread; } @Override public Order.StyleOrder size(Size size) { Objects.requireNonNull(size, "Size is required"); this.size = size; return this; } @Override public Order.StyleQuantityOrder vegan() { createSandwich(SandwichStyle.VEGAN); return this; } @Override public Order.StyleQuantityOrder meat() { createSandwich(SandwichStyle.MEAT); return this; } @Override public Order.DrinksOrder quantity(int quantity) { if (quantity <= 0) { throw new IllegalArgumentException("You must request at least one sandwich"); } this.quantity = quantity; return this; } @Override public Checkout softDrink(int quantity) { if (quantity <= 0) { throw new IllegalArgumentException("You must request at least one sandwich"); } this.drinkQuantity = quantity; this.drink = new Drink(DrinkType.SOFT_DRINK, pricingTables.getPrice(DrinkType.SOFT_DRINK)); return checkout(); } @Override public Checkout cocktail(int quantity) { if (quantity <= 0) { throw new IllegalArgumentException("You must request at least one sandwich"); } this.drinkQuantity = quantity; this.drink = new Drink(DrinkType.COCKTAIL, pricingTables.getPrice(DrinkType.COCKTAIL)); return checkout(); } @Override public Checkout softDrink() { return softDrink(1); } @Override public Checkout cocktail() { return cocktail(1); } @Override public Checkout noBeveragesThanks() { return checkout(); } private Checkout checkout() { MonetaryAmount total = sandwich.getPrice().multiply(quantity); if (drink != null) { MonetaryAmount drinkTotal = drink.getPrice().multiply(drinkQuantity); total = total.add(drinkTotal); } return new Checkout(sandwich, quantity, drink, drinkQuantity, total); } private void createSandwich(SandwichStyle style) { MonetaryAmount breadPrice = pricingTables.getPrice(this.bread); MonetaryAmount sizePrice = pricingTables.getPrice(this.size); MonetaryAmount stylePrice = pricingTables.getPrice(SandwichStyle.VEGAN); MonetaryAmount total = breadPrice.add(sizePrice).add(stylePrice); this.sandwich = new Sandwich(style, this.bread, this.size, total); }}结果是一个 API,它将直接直观地将请求返回给我们。
Checkout checkout = Order.bread(Bread.PLAIN) .size(Size.SMALL) .meat() .quantity(2) .softDrink(2);Fluent API 与其他模式有何不同?
对两种 API 标准进行比较是很普遍的,它们是 Builder 和 Fluent-API。原因是它们在创建实例的过程中都按顺序使用方法。
但是,Fluent-API 是"与 DSL 相关联的",它强制采用一种简单的方法来实现这一点。但为了使这些差异更加明显,我们为每个模式分别列出了亮点:
Builder 模式:
它往往更容易实施;
不清楚需要哪些施工方法;
绝大多数问题都会在运行时发生;
一些工具和框架会自动创建它;
它需要在 build 方法中进行更健壮的验证,以检查哪些强制方法没有被调用。
流利的API:
重要的是,对于每个方法,都有验证,如果参数无效则抛出错误,记住快速失败的前提;
它必须在过程结束时返回一个有效的对象。
现在,是否更容易理解模式之间的异同?
这就是我们对 fluent-API 概念的介绍。与所有解决方案一样,没有"灵丹妙药",因为整个过程通常不合理。
它是一个出色的工具,有助于为你和其他用户创建故障保护。
感谢各位的阅读!关于"如何使用Fluent API创建更简单、更直观的代码"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!