面向对象特别有价值,因为会出现这些类型的场景,并且它为您提供了合理设计抽象的工具,从而可以封装复杂性。
真正的问题是,您在哪里封装这种复杂性?
因此,让我退后一步,谈谈我在这里指的是什么“复杂性”。您的问题(据我了解;如果我错了,请纠正我)是一个持久性模型,该模型对于完成数据所需的任务不是有效可用的模型。它可能对其他任务有效且可用,但对您的任务无效。
那么,当我们拥有的数据不能为我们的方法提供良好的模型时,我们该怎么办?
翻译。这是您唯一可以做的。这种翻译就是我上面提到的“复杂性”。因此,既然我们接受了将要转换的模型,我们需要确定几个因素。
我们需要同时翻译两个方向吗?两种方向的翻译方式是否相同:
(Tbl A,Tbl B)-> Obj X(读取)
对象X->(Tbl A,Tbl B)(写)
还是插入/更新/删除活动代表不同类型的对象,以便您以Obj X的形式读取数据,但是从Obj Y插入/更新了数据?其中,你不妨去这两种方式,如果没有更新/插入/删除可能是重要的因素,在那里,你想放的翻译。
您在哪里翻译?
回到我在这个答案中所做的第一句话;OO允许您封装复杂性,而我在这里指的是事实,您不仅应该,而且如果您希望确保它不会泄漏并渗入所有代码中,则必须封装该复杂性。同时,重要的是要认识到您不能拥有完美的抽象,因此不必担心拥有一个非常有效和可用的抽象。
现在又 您的问题是:您将这种复杂性放在哪里?好吧,你有选择。
你可以做到这一点在使用存储过程的数据库。这具有经常不能很好地与ORM配合使用的缺点,但这并不总是正确的。存储过程可以带来一些好处,包括经常带来的性能。但是,存储过程可能需要大量维护,但是要由您来分析您的特定方案,并确定维护是否比其他选择更多或更少。我个人对存储过程非常熟练,因此,可用人才的这一事实减少了开销。永远不要低估根据您所知道的事情做出决策的价值。有时,次优解决方案可能比正确的解决方案更理想,因为您或您的团队比最佳解决方案更了解如何创建和维护它。
数据库中的另一个选项是视图。取决于您的数据库服务器,这些可能是高度最佳或次优的,甚至根本无效,缺点之一可能是查询时间,具体取决于数据库中可用的索引选项。如果您不需要进行任何数据修改(插入/更新/删除),则视图将成为更好的选择。
越过数据库,您将拥有使用存储库模式的旧时机。这是一种经过时间检验的方法,非常有效。缺点往往包括样板,但是结构合理的存储库可以避免这种情况,即使这些确实导致不幸的样板数量,存储库也往往是简单的代码,易于理解和维护,并提供良好的API /抽象。另外,存储库还具有单元可测试性,这是数据库内选项所失去的。
诸如自动映射器之类的工具可能使使用ORM可行,从而可以在数据库模型从orm到可用模型之间进行转换,但是其中一些工具在维护/理解行为方面有些棘手,更像魔术。尽管它们可以使开销代码最小化,但在很好理解的情况下却可以减少维护开销。
接下来,您将从数据库中走得越来越远,这意味着将有更多的代码要处理未翻译的持久性模型,这将是真正令人不快的。在这些情况下,您要谈论的是将翻译层放在您的UI中,听起来好像您现在正在做。这通常是一个非常糟糕的主意,并且会随着时间的流逝而恶化。
现在让我们开始疯狂地讲话。
该Object
不是唯一的终端都是,所有的抽象存在。多年来,计算机科学的研究甚至在此之前的数学研究中,都已经有了大量的抽象概念。如果要开始发挥创意,让我们开始谈论已经研究过的已知抽象。
有演员模型。这是一种有趣的方法,因为它说您要做的就是将消息发送到其他代码,从而将所有工作有效地委派给该其他代码,这对于封装所有代码的复杂性非常有效。只要您向演员发送一条消息,说“我需要将Obj X发送到Y”,并且您有一个容器等待位置Y的响应,然后处理Obj X,就可以这样做。您甚至可以发送一条消息来指示“我需要Obj X并完成计算Y,Z”,然后您甚至不需要等待;翻译发生在消息传递的另一端,如果您不需要阅读结果,可以继续进行翻译。出于您的目的,这可能会稍微滥用参与者模型,但这要视情况而定;
另一个封装边界是过程边界。这些可以非常有效地用于隔离复杂性。您可以将转换代码创建为Web服务,其中的通信是使用SOAP,REST的简单HTTP,或者如果您确实想要自己的协议(不建议)。STOMP并不是一个较差的较新协议。或者,将普通的守护程序服务与系统本地的公共存储管道一起使用,以使用您选择的任何协议再次快速通信。这实际上有一些不错的好处:
- 您可以运行多个进程,同时为旧版本和较新版本提供翻译支持,从而允许您更新翻译服务以发布对象模型V2,然后在以后单独更新使用代码以与新对象一起使用模型。
- 您可以做一些有趣的事情,例如将流程固定在性能核心上,通过使该流程成为唯一一个具有安全性特权运行的流程来接触该数据,还可以通过这种方法获得一定程度的安全性。
- 当您在谈论过程边界时,您将获得一个非常牢固的边界,该边界将保持固定,从而确保长时间以来将抽象泄漏降至最低,因为在翻译空间中编写代码将无法在翻译空间之外被调用,因为它们不会共享流程范围,从而确保按合同确定一组固定的使用方案。
- 异步/非阻塞更新的能力更加简单。
缺点显然是比通常需要的维护更多,通信开销会影响性能和维护。
封装复杂性的方法有很多种,可以将复杂性放在系统中越来越奇怪和奇怪的地方。使用高阶函数的形式(通常使用策略模式或各种其他奇怪形式的对象模式来伪装),您可以做一些非常有趣的事情。
是的,让我们开始谈论一个单子。您可以以非常小巧的特定功能的独立方式创建此转换层,这些功能执行必要的独立转换,但将所有这些转换功能隐藏在看不见的地方,因此外部代码几乎无法访问它们。这样做的好处是减少了对它们的依赖,使它们可以轻松更改而不会影响太多外部代码。然后,您创建一个可以接受任何高级OO模型类型对象的高阶函数(匿名函数,lambda函数,策略对象,但是您需要对其进行结构化)的类。然后,使用适当的转换方法让接受这些函数的基础代码执行文字执行。
这将创建一个边界,其中所有翻译不仅存在于边界的另一端,而且远离您的所有代码。它仅在那一侧使用,从而使您的其余代码甚至不知道有关该边界的入口点在哪里的任何信息。
好吧,是的,这的确在发疯,但谁知道呢?您可能只是疯了(认真地,不要从事疯狂程度低于88%的单子,这确实有造成人身伤害的危险)。