将代码逻辑与实际数据结构分开。最佳做法?

时间:2010-05-18 08:50:45

标签: oop

我有一个将大量数据加载到内存中的应用程序(这是因为它需要对大数据集执行一些数学模拟)。这些数据来自几个数据库表,它们都是相互引用的。

数据的一致性规则相当复杂,查找所有相关数据需要对数据进行相当多的哈希和其他附加数据结构。

问题是用户还可以在对话框中以交互方式更改此数据。当用户按下OK按钮时,我想执行所有检查以确定他没有在数据中引入不一致。实际上,所有数据都需要立即检查,因此我无法逐步更新我的数据集并逐个执行检查。

但是,所有检查代码都在加载到内存中的实际数据集上工作,并使用散列和其他数据结构。这意味着我必须做以下事情:

  • 从对话框中获取用户的更改
  • 将它们应用于大数据集
  • 对大数据集执行检查
  • 如果检查失败,则撤消所有更改

我不喜欢这个解决方案,因为其他线程也在不断使用数据集,我不想在执行检查时暂停它们。此外,撤销意味着需要搁置旧情况,这也是不可能的。

另一种方法是将检查代码与数据集分开(并让它在明确给定的数据上工作,例如来自对话框),但这意味着检查代码不能使用散列和其他附加数据结构,因为它们只是处理大数据集,使检查更慢。

在将复杂数据应用于“应用程序”数据集之前检查用户对复杂数据的更改有什么好的做法?

4 个答案:

答案 0 :(得分:4)

这可能现在没有多大帮助,因为您的应用已经构建,并且您可能不想重新实现,但我会提及它以供参考。

使用ORM框架可以帮助您。它不仅处理从数据库获取数据到面向对象的表示,它还提供了实现孤立的临时更改和视图的工具:

  • 将ORM框架与事务一起使用,您可以允许用户在不影响其他用户的情况下更改模型中的对象,并且在检查之前不“提交真实”数据。 ACID交易保证确保您的更改不会持久保存到数据库中,而是保存在您的交易中,只有您可以看到。然后,您可以对数据运行检查,并仅在数据验证时提交事务。如果数据未验证,则回滚事务并放弃更改。如果确实验证,则提交事务并使更改成为永久更改。

  • 或者,您可以创建提供验证数据的视图。视图组合了基础数据和临时表(当前连接的本地)。这样可以避免锁定表,代价是必须编写和维护视图。

编辑:如果您已在内存中拥有丰富的对象模型,那么支持增量,本地和隔离更改的最难的部分是对象之间的直接引用。当您想要将对象A替换为包含更改的A'时,您不希望对所有引用进行深层复制,因为您提到对象模型很大。此外,您不希望必须更新指向A的所有对象以引用A'。例如,考虑一个非常大的双向链表。如果只更改了一个元素,则无法创建与旧的列表相同的新列表,而无需复制整个列表。您可以通过存储相关对象的标识符而不是对象本身来实现隔离。例如。您的协作者不会明确地引用A,而是存储对标识A,键(A)的唯一键的引用。此键用于在需要时(例如在验证期间)获取实际对象。然后,您的模型将成为对象的大键映射,可以为本地更改进行修饰。按键查找对象时,首先检查本地地图的值,如果没有找到,请检查通用地图。要将A更改为A',请在本地地图中添加一个条目,将键(A)映射到A'。 (注意A和A'具有相同的键,因为从逻辑上讲它们是相同的项。)当您运行验证代码时,然后合并本地更改,因为引用键(A)的对象将获得A',而其他用户使用键(A)将获得原始,A。

这可能听起来很复杂,但通过删除显式引用并按需计算它们是支持隔离更新的唯一方法,而无需对数据进行深层复制。

另一种替代方法是,验证器在使用它们之前使用映射来查找具有替换的对象。例如。您的用户修改了A,因此您将A-> A'放入地图中。验证器正在遍历模型并遇到A.在使用A之前,它会检查地图,然后找到A',然后使用它。这种方法的难点在于,每次使用对象之前都必须确保检查地图。如果您错过了一个,那么您对模型的看法将不一致。

答案 1 :(得分:3)

我会尝试在将应用到数据集之前验证更改,因为撤消后来证明无效的更改的连锁反应很容易成为一场噩梦。

如果确实存在大量数据,我理解创建它的完整副本可能并不可行 - 尽管通常“写入时复制”将是最简单和最安全的解决方案。如果您真的只能通过考虑整个数据集来验证更改,那么您可以尝试一种“类似装饰器”的方法,即以某种方式创建分层更改的“视图”现有数据体的顶部,而不实际修改后者。这可以用于验证更改,如果验证成功,您可以实际应用更改;否则你可以简单地扔掉“视图”和更改,而不会以任何方式影响原始数据。

答案 2 :(得分:0)

嗯,我建议而不是将数据加载到内存中。这很昂贵,但允许您同时处理所有数据。当数据更改有效时,只需使用某种锁定策略将更改从副本应用于所有数据。这样,只要您可以原子方式应用更改,就不需要任何撤消操作。如果您的需求更复杂,您甚至可以尝试一些交易系统。 还要考虑延迟加载(复制)您真正需要的数据。最后我想到的是,如果您需要使用事务处理来自数据库的大型数据集,请尝试使用Prolog。将chcecks表示为谓词可能是合理的。

答案 3 :(得分:0)

听起来好像应该将规则等移动到它们所属的数据库,通过在我们的应用程序中进行检查,您将始终发出。而是通过将多少逻辑放在例如用户插入值时运行的存储过程中,您可以捕获并回滚无效输入。但我想你有理由把它全部留在记忆中。