guice注入带参构造器_带有Guice的富域模型

guice注入带参构造器

贫血域模型是一个非常常见的反模式。 在ORM和DI框架的世界中,我们自然会发现自己拥有一个由ORM管理的“域”,该域包含所有数据且无行为。 通过我们的DI框架有帮助地注入了辅助类,这些辅助类都是行为且没有数据。

在本文中,我将介绍一种实现富域模型的可能方法-此示例使用Guice ,尽管我确信Spring等将具有实现同一目标的不同方法。

问题

所有源代码都可以在github上找到。 “ master”分支显示原始的,分解不良的代码。 “富域”分支显示了我描述的解决方案。

贫血领域模型

首先,我们的贫血领域模型– TradeOrder.java 。 与Hibernate一样,该类具有大量的注释,这些注释描述了数据模型,所有数据的字段,访问数据的访问器和变异器,而没有其他任何有意义的东西。 我假设在此领域中,TradeOrders可以完成任务。 也许我们下订单或取消订单。 沿线某处,我们域中的关键对象可能应该具有某些行为。

@Entity
@Table(name="TRADE_ORDER")
public class TradeOrder {@Id@Column(name="ID", length=32)@GeneratedValueprivate String id;@ManyToOne@JoinColumn(name="CURRENCY_ID", nullable=false)@ForeignKey(name="FK_ORDER_CURRENCY")@AccessType("field")private Currency currency;@Column(name="AMOUNT", nullable=true)private BigDecimal amount;public TradeOrder() { }public String getId() { return id; }public Currency getCurrency() { return currency; }public void setCurrency(Currency currency) { this.currency = currency; }public BigDecimal getAmount() { return amount; }public void setAmount(BigDecimal amount) { this.amount = amount; }
}

助手类

在这个非常简单的示例中,我们需要计算订单的价值,即我们要购买(或出售)的股票数量和所支付的每股价格。 不幸的是,由于这涉及到依赖关系,因此行为并不驻留在与其相关的类中,而是被转移到了一个非常有用的帮助程序类中。

看一下FiguresFactory.java 。 该类只有一个公共方法– buildFrom。 此方法的目标是从TradeOrder创建图形。

public Figures buildFrom(TradeOrder order, Date effectiveDate)throws OrderProcessingException {Date tradeDate = order.getTradeDate();HedgeFundAsset asset = order.getAsset();BigDecimal bestPrice = bestPriceFor(asset, tradeDate);return order.getType() == TradeOrderType.REDEMPTION? figuresFromPosition(order,lookupPosition(asset, order.getFohf(), tradeDate),lookupPosition(asset, order.getFohf(), effectiveDate),bestPrice): getFigures(order, bestPrice, null);
}

除了“生效日期”(无论可能是什么)之外,此方法唯一需要输入的就是TradeOrder。 使用TradeOrder上大量的吸气剂,它会要求操作数据,而不是告诉 TradeOrder需要什么。 在理想的,面向对象的系统中,这应该是TradeOrder上称为createFigures的方法。

我们为什么到这里来? 都是依赖注入框架的错! 因为创建Figures对象的过程需要我们解析价格和货币,所以我们需要使用可注入的依赖关系去查找这些数据。 我们的贫血领域无法注入依赖项,因此我们将其注入到这个小助手类中。

我们最终得到的是经典的贫血域模型。 TradeOrder具有数据; 而许多辅助类(例如FiguresFactory)包含对数据进行操作的行为。 都是非OO的。

更好的方法

资料记录

第一步是创建一个简单的值对象以映射数据库中的行-我将其称为TradeOrderRecord.java 。 这看起来很像原始域对象,只不过我删除了访问器和更改器以明确表明这是一个没有行为的值对象。

为了使构造这些记录对象更加容易,我使用了karg,这是我的一位同事编写的库–这要求我们声明可用于构造记录的一组参数,以及一个接受参数列表的构造函数。 这极大地简化了构造,并且避免了我们拥有需要27个字符串的构造函数(例如)。

@Entity
@Table(name="TRADE_ORDER")
public class TradeOrderRecord {@Id@Column(name="ID", length=32)@GeneratedValuepublic String id;@Column(name="CURRENCY_ID")public String currencyId;@Column(name="AMOUNT", nullable=true)public BigDecimal amount;public static class Arguments {public static final Keyword<String> CURRENCY_ID = newKeyword();public static final Keyword<BigDecimal> AMOUNT = newKeyword();}protected TradeOrderRecord() { }public TradeOrderRecord(KeywordArguments arguments) {this.currencyId = Arguments.CURRENCY_ID.from(arguments);this.amount = Arguments.AMOUNT.from(arguments);}
}

富域

我们的目标是使TradeOrder成为一个丰富的域对象-它应该具有与“ TradeOrder”的域概念相关的所有行为和数据。

数据

TradeOrder首先需要在内部存储与TradeOrder相关的所有数据(至少作为起点,未使用的字段暗示我们可能能够进一步简化此操作)。

public class TradeOrder {private final String id;private final String currencyId;private final BigDecimal amount;

我们使数据不可变 。 不可变状态通常是一件好事–在这里,它迫使我们清楚这是一个完全填充的TradeOrder,并且由于它具有ID,因此它始终与数据库中的一行关联。 通过使TradeOrder不可变,显而易见的问题是–如何更新订单? 嗯,我们可以选择多种方式来做到这一点–但这是在不同时间的不同故事。

我们也不需要访问器。 由于我们计划将与TradeOrder相关的所有行为放在TradeOrder类本身上,因此其他类不需要询问信息,它们只需要告诉我们他们想要实现什么。

注意:有一个(现在不建议使用)访问器–暗示应进一步移动的行为。

依存关系

除了存储数据的字段外,TradeOrder还具有表示可注入依赖项的字段。

private final CurrencyCache currencyCache;
private final PriceFetcher bestPriceFetcher;
private final PositionFetcher hedgeFundAssetPositionsFetcher;
private final FXService fxService;

有些人会发现这种冒犯性-将依赖项与数据混合在一起。 但是,就我个人而言,我认为这种权衡是值得的–能够在与之相关的类上定义我们的行为的好处是值得的。

行为

现在我们将数据和依赖项全部集中在一处,在FiguresFactory中的方法之间移动相对容易:

public Figures createFigures(Date effectiveDate) throws OrderProcessingException {BigDecimal bestPrice = bestPriceFor(this.asset, this.tradeDate);return this.type == TradeOrderType.REDEMPTION? figuresFromPosition(fohf,lookupPosition(this.asset, fohf, this.tradeDate),lookupPosition(this.asset, fohf, effectiveDate), bestPrice): getFigures(fohf, bestPrice, null);
}

施工

我们需要解决的最后一件事是如何创建TradeOrder实例。 由于数据和相关性字段均标记为final,因此构造函数必须将它们全部初始化。 这意味着我们需要一个带有依赖关系的构造函数和一个TradeOrderRecord(即我们从数据库中读取的值对象):

@Inject
protected TradeOrder(CurrencyCache currencyCache,PriceFetcher bestPriceFetcher,PositionFetcher hedgeFundAssetPositionsFetcher,FXService fxService,@Assisted TradeOrderRecord record) {...
}

这不是特别漂亮,但是要注意的关键是@Assisted注释。 这使我们可以告诉Guice,其他依赖项已正常注入,而TradeOrderRecord应该从工厂方法传递过来。 工厂界面本身如下所示:

public static interface Factory {TradeOrder create(TradeOrderRecord record);
}

我们不需要实现此接口,Guice会自动提供它。 当需要创建TradeOrder实例时,可以从其他地方使用TradeOrder.Factory成为可注入依赖项。 Guice会像平常一样初始化可注入依赖关系,辅助依赖关系– TradeOrderRecord –从工厂传递过来。 因此,我们的调用代码无需担心我们的富域需要可注入的依赖项。

@Inject private TradeOrder.Factory tradeOrderFactory;
...
TradeOrderRecord record = tradeOrderDAO.loadById(id);
TradeOrder order = tradeOrderFactory.create(record);

结论

通过将依赖项和数据混合到一个丰富的域模型中,我们可以定义具有正确行为的类。 现在,TradeOrder中明显的代码味道是创建图形的详细机制可能是一个单独的问题,应予以分解。 没关系,我们可以引入一个新的依赖项来进行管理-只要TradeOrder仍然是构造Figures对象的起点。

通过将行为放在一个地方,我们的域模型更易于使用,更易于推理,也更容易发现重复或相似的情况。 然后,我们可以使用一个好的对象模型来进行合理的重构,而不是将任意的区别引入到作为函数库而不是域参与者的帮助器类中。

参考: 实施富域M 来自JCG合作伙伴 David Green的Guice先生在Actively Lazy Blog中 与 oice合作 。

相关文章 :
  • 在领域驱动的设计,贫乏的领域模型,代码生成,依赖项注入等方面……
  • 在域驱动设计中使用状态模式
  • Spring和AspectJ的领域驱动设计
  • Spring依赖注入技术的发展
  • 什么是依赖倒置? 是IoC吗?

翻译自: https://www.javacodegeeks.com/2011/10/rich-domain-model-with-guice.html

guice注入带参构造器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/358058.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

转:巧用搜狗输入法输入英文单词

转&#xff1a; http://www.techweb.com.cn/digi/experience/2013-06-03/1300700.shtml 你是否遇到过这样的情形&#xff1a;想打一个英文单词&#xff0c;但是忘了具体拼写是什么&#xff0c;只是记了个大概?比如“竞争”&#xff0c;到底是competetion还是competition呢?又…

服务器ping不通的解决办法之阿里云云服务器VNC报错Failed to execute /sbin/init

背景:最近买了一个阿里云的云服务器,今天想安装阿里云的操作文档搭建一个博客网站来着,发现服务器公网IP都ping不通 解决办法参考: 方法一:如何解决ping请求超时的问题 方法二:本地ping服务器连接不到,总是超时 我的防火墙已经关闭,内网切换移动网络也不行,安全组…

从ofcms的模板注入漏洞(CVE-2019-9614)浅析SSTI漏洞(freemarker模板)

https://www.cnvd.org.cn/flaw/show/CNVD-2019-08488 思路: 1、pom.xml的时候发现存在模版引擎freemarker http://t.zoukankan.com/Eleven-Liu-p-12747908.html 2、寻找修改模版的地方 TemplateController.java 3、添加执行Payload <#assign ex“freemarker.template.utilit…

集合中的可选

有时有人认为Optional类型值得在集合中使用。 据称&#xff0c;它解决了以下问题&#xff1a; HashMap在没有键映射以及值null映射到键的情况下返回null 。 如果使用Map<Optional<Something>>则可以清楚地区分缺少的映射和缺少的值。 这样一来&#xff0c;您在兔子…

当你不知道今天星期几,不妨在编辑器写下这段代码

背景:最近加班比较严重,天天废寝忘食的写代码,不知春夏,看看今天星期几啦,实现方式很多,下面演示一下switch的方式 html: <!DOCTYPE html> <html><head><meta charset="utf-8"><title>孙叫兽测试switch语句</title></…

05.html学习-表单

表单标签&#xff1a; 表单标签的作用是用于提交数据给服务器的。 表单标签的根标签是<form>标签常用的属性&#xff1a; action: 该属性是用于指定提交数据的地址。 method&#xff1a; 指定表单的提交方式。 get : 默认使用的提交方式。 提交的数据会显示在地址栏上。 …

在Log4j2中更好地执行非日志记录器调用

使用Log4j 1.x并希望避免在某些情况下可能会造成额外的性能影响&#xff08;即使实际上未记录该消息&#xff09;时&#xff0c;通常使用日志记录防护 。 Java的简单日志记录外观 &#xff08; SLF4J &#xff09;带给Java日志记录的最吸引人的功能之一是能够减少需要进行这些日…

javaScript学习笔记之运算符

运算符 = 用于赋值。用于给 JavaScript 变量赋值。 运算符 + 用于加值。算术运算符 + 用于把值加起来。 下面展示了算术运算符及赋值运算符: <!DOCTYPE html> <html><head><meta charset="utf-8" /><title>孙叫兽测试运算符</…

编写高质量代码-OC 第7章 设计模式与Cocoa编程

45、设计模式是特定环境下的特定问题的解决方案46、MVC模式是一种复合或聚合模式47、对象建模在数据库中也广泛使用48、类簇可简化框架的公开架构而又不减少功能的丰富性 1、类簇基于抽象工厂设计模式2、类簇&#xff0c;可以用于隐藏实现的详细细节&#xff0c;为调用者提供一…

Goby反制复现

0x00 前言 最近复现Goby反制的时候遇到很多坑&#xff0c;记录一下反制过程以及遇到的坑点&#xff0c;还有世界上最强的黑客mux1ng帮我解决了很多问题。 0x01环境 攻击机&#xff1a; windows10 Goby1.8.230 172.20.10.3反制机&#xff1a; Windows7 Phpstudy2016 172.20.…

测试双重图案

前段时间&#xff0c;我写了一篇有关使用Test Double的后果的文章&#xff0c;但是与Test Double Patterns无关&#xff0c;仅是一个简单的清单。 今天&#xff0c;我想对其进行更改&#xff0c;并解释这些模式之间的差异。 正如我在提到的文章中写道&#xff1a; Test Doubl…

javaScript学习笔记之比较运算符||逻辑运算符||条件运算符(三目运算符)

比较运算符在逻辑语句中使用,以测定变量或值是否相等。 逻辑运算符用于测定变量或值之间的逻辑。 javaScript基于某些条件对变量进行赋值的条件运算符(三目运算符)。 HTML: <!DOCTYPE html> <html> <head><meta charset="utf-8"><title…

类的依赖注入

http://www.360doc.com/content/14/0421/09/10504424_370757998.shtml转载于:https://www.cnblogs.com/changbaishan/p/4949225.html

JDK 9:模块系统状态的重点

马克雷因霍尔德 &#xff08; Mark Reinhold &#xff09;的“模块系统状态 &#xff08;SOMS&#xff09;”已于本月初发布&#xff0c;它提供了信息丰富的可读性“对项目Jigsaw中原型的Java SE平台进行了增强的非正式概述&#xff0c;并被提议作为JSR 376的起点。” 在这篇文…

fckeditor漏洞_三十,文件上传漏洞、编辑器漏洞和IIS高版本漏洞及防御

一.编辑器漏洞 1.编辑器 编辑器属于第三方软件&#xff0c;它的作用是方便网站管理员上传或编辑网站上的内容&#xff0c;类似我们电脑上的Word文档。 编辑器通常分为两种情况&#xff1a; (1) 不需要后台验证&#xff0c;可以直接在前台访问且操作。通过方法找到编辑器&#x…

java学习笔记之条件语句(if...else)

条件语句用于基于不同的条件来执行不同的动作。 通常在写代码时,您总是需要为不同的决定来执行不同的动作。您可以在代码中使用条件语句来完成该任务。 在 JavaScript 中,我们可使用以下条件语句: if 语句 - 只有当指定条件为 true 时,使用该语句来执行代码if...else 语句 …

iOS 学习之NSPredicate

电话号码验证表达式 (BOOL)validateMobile:(NSString *)mobileNum { /** * 手机号码 * 移动&#xff1a;134[0-8],135,136,137,138,139,150,151,157,158,159,182,187,188 * 联通&#xff1a;130,131,132,152,155,156,185,186 * 电信&#xff1a;133,1349,153,180,189 */ NSStr…

javaScript学习笔记之break 和 continue 语句对比

break 语句用于跳出循环。 continue 用于跳过循环中的一个迭代。 break 语句可用于跳出循环。 break 语句跳出循环后,会继续执行该循环之后的代码(如果有的话): continue 语句中断循环中的迭代,如果出现了指定的条件,然后继续循环中的下一个迭代。 代码: <!DOCTYPE …

畅捷通T+任意文件上传(CNVD-2022-60632 )漏洞复现

一、漏洞描述 2022年8月29日和8月30日&#xff0c;畅捷通公司紧急发布安全补丁修复了畅捷通T软件任意文件上传漏洞。未经身份认证的攻击者利用该漏洞&#xff0c;通过绕过系统鉴权&#xff0c;在特定配置环境下实现任意文件的上传&#xff0c;从而执行任意代码&#xff0c;获得…

Spring 3使用JUnit 4进行测试– ContextConfiguration和AbstractTransactionalJUnit4SpringContextTests...

在Internet上寻找一种测试我的Spring 3应用程序的方法&#xff0c;我找到了许多描述如何使用JUnit测试应用程序的文章。 它们中的大多数都是不完整的示例&#xff0c;实际上并不起作用。 在这篇文章中&#xff0c;我将尝试填补这一空白&#xff0c;并撰写一篇简洁而简单的文章&…