您见过那些具有许多属性的巨大物体吗?这些域对象由于不希望从数据库中检索太多信息而正在使用延迟加载?我敢打赌,您有这种可疑的乐趣。
今天,我想分享一下我对它们的印象: 使用延迟加载应被视为一种代码味道!
让我解释一下自己:
延迟加载意味着有时您不需要某个对象的某些属性。这些属性将在不同的上下文中是必需的。这是否意味着您要根据上下文构建不同的对象?
使用此对象的功能肯定知道太多。它知道对象的API,并且此API还包含需要未加载属性的方法。很好,不是吗?
您必须记住每个地方需要什么,不需要什么…
…而且,更糟糕的是,您必须记住您可能使用的功能以及特定位置不支持的方法。
如果您还不够,请让我详细说明。
延迟加载的工作方式
简而言之, 延迟加载 允许您在加载父级时不加载子级。仅当您明确要求时才加载它们。
它是如何工作的?让我们看一个简单的例子:
class User {
private final Name name;
@OneToMany(fetch = FetchType.LAZY)
private List roles;
@OneToMany(fetch = FetchType.LAZY)
private List subscriptions;
// Some more attributes and methods
}
此类的定义告诉您什么?FetchType.LAZY对我们意味着什么?这为我们提供了包含用户角色和订阅的列表的信息,除非我们明确要求此类数据,否则这些数据将不会被填充。
什么是有限的上下文?
受限上下文是域驱动开发中的主要模式之一。通过将它们分为不同的上下文,它可以帮助您使用大型域模型。由于这个原因,您的域对象变得更小,应用程序的业务逻辑变得更容易理解。
代码气味?但…
在前面的段落之一中,我写了User类的定义告诉我们的内容。到现在为止,一切都与该机制有关。现在我们可以走得更远。
让我们再来看一下我们的课:
class User {
private final Name name;
@OneToMany(fetch = FetchType.LAZY)
private List roles;
@OneToMany(fetch = FetchType.LAZY)
private List subscriptions;
// Some more attributes and methods
}
除了已经提到的内容之外,您能告诉我更多有关此对象的信息吗?
我们知道我们正在使用其对象在可能需要但不一定需要角色的地方使用的类。可能需要订阅但不一定要订阅的地方。名称始终是必需的。
我们知道,在我们的应用程序/环境中有一些功能/位置需要这些属性,而在某些地方这些属性是无用的。
但是……我们必须遍历代码才能找到那些地方。这需要时间和精力。不幸的是,我们还有机会错过一些地方。
我们所知道的(和我们所不知道的)
知道在哪里和需要什么会更好吗?当然可以!问题是:我们如何实现它?
让我们对示例进行简短分析:
class User {
private final Name name;
@OneToMany(fetch = FetchType.LAZY)
private List roles;
@OneToMany(fetch = FetchType.LAZY)
private List subscriptions;
// Some more attributes and methods
}
我们已经知道一些事情:
名称始终是必需的。
有时我们需要角色。
有时我们需要订阅。
基于此信息,我们可以添加一件事- 我们知道我们并不总是需要所有这些信息。也许听起来有些琐碎,但这也很重要。
这就是信息。现在是未知的时候了:
在哪里我们既需要角色又需要订阅?
在不同的地方需要角色和订阅吗?
有没有我们不需要的地方?
是否取决于上下文需要什么属性?
未知数的问题是我们必须遍历代码才能找到答案。但这还不是问题的终点。当您最终找到这些地方时,没有方法或变量或任何可重命名的信息,不会在一段时间内丢失此信息。下次,您将不得不重复该工作。
让我们完善代码
由于上一段中列出了未知数,因此更改现有代码(真正的代码)以及我们正在使用的代码并不容易。这就是为什么我建议您在考虑延迟加载之后立即进行此更改。那是最便宜的改进的正确时机。
好的,但是我们如何才能改进示例中的代码?
首先要做的是找到未知数的答案。没有这些答案,我们就无法前进。在我们的案例中,我将假设我们认识到三种不同的情况:
身份验证和授权是我们需要用户名及其角色的地方。
我们在处理报告发送的地方需要用户名及其订阅。
在我们应用程序的其他领域,我们不需要角色或订阅。
现在,我们可以重构User类并将其拆分为更容易理解的内容:
4
现在我们有了三个类,而不是一个,但是我们的代码中也有更多信息。我们无需遍历代码即可找出所需内容和位置。打开类的定义就足够了
下一步是什么?
不幸的是,要达到您所在域的状态,您必须付出很多努力。为什么?主要是因为未知。应用程序越大,获取所有信息的难度就越大。这就是为什么我鼓励您在考虑将延迟加载作为解决方案之后立即拆分类。
如果您的域中已经有延迟加载的引用,则应仅重构已经使用的部分。您将使更改的风险和进行更改所需的工作最小化。无论如何,代码将变得更具描述性。
祝好运!
最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。