代码之家  ›  专栏  ›  技术社区  ›  Patrick

将代码逻辑与实际数据结构分离。最佳实践?[关闭]

oop
  •  5
  • Patrick  · 技术社区  · 15 年前

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

    数据的一致性规则相当复杂,查找所有相关数据需要相当多的散列和其他数据结构。

    问题是,用户也可以在对话框中以交互方式更改这些数据。当用户按下OK按钮时,我想执行所有检查,以确保他没有在数据中引入不一致性。实际上,所有的数据都需要一次检查,所以我不能增量地更新我的数据集并逐个执行检查。

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

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

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

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

    在将复杂数据应用到“应用程序”数据集之前,检查用户对这些数据所做的更改是什么好做法?

    4 回复  |  直到 15 年前
        1
  •  4
  •   mdma    15 年前

    现在这可能没什么帮助,因为你的应用程序已经构建好了,你可能不想重新实现,但我会提到它作为参考。

    使用ORM框架将在这里帮助您。它不仅处理将数据库中的数据转换为面向对象的表示,还提供了实现独立临时更改和视图的工具:

    • 使用带有事务的ORM框架,您可以允许用户在不影响其他用户的情况下更改模型中的对象,并且在检查数据之前不“实际”提交数据。事务的ACID保证确保您的更改不会持久化到数据库,而是保存在事务中,只对您可见。然后,您可以对数据运行检查,并且只有在数据验证之后才提交事务。如果数据未验证,则回滚事务并放弃更改。如果它确实进行了验证,那么您就提交了事务,并且更改是永久的。

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

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

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

    另一种类似的方法是,验证程序在使用对象之前使用映射查找替换对象。例如,您的用户修改了,因此您将->a'放入地图中。验证程序正在对模型进行迭代,并遇到一个。在使用之前,它检查映射,并找到一个',然后使用它。这种方法的难点在于,每次使用对象之前都必须检查地图。如果您错过了一个,那么您对模型的看法将不一致。

        2
  •  3
  •   Péter Török    15 年前

    我会尝试任何方法来验证更改 之前 将它们应用到数据集中,因为撤销变更的涟漪效应(后来被证明是无效的)很容易成为一场噩梦。

    如果真的有很多数据,我理解创建它的完整副本可能是不可行的——尽管一般来说,“写时复制”是最简单和最安全的解决方案。如果你 真正地 只有在考虑到整个数据集的情况下才能验证更改,您可以尝试一种类似于“装饰器”的方法,即通过某种方式创建分层在现有数据体之上的更改的“视图”,而不实际修改后者。这可以用于验证更改,如果验证成功,则可以实际应用更改;否则,您只需丢弃“视图”和更改,而不会以任何方式影响原始数据。

        3
  •  0
  •   Gabriel Ščerbák    15 年前

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

        4
  •  0
  •   AndersK    15 年前

    听起来好像你应该把规则等转移到它们所属的数据库中,通过在我们的应用程序中进行检查,你会一直发出问题。相反,通过在实例存储过程中放置尽可能多的逻辑,这些存储过程在用户插入可能捕获和回滚无效输入的值时运行。但我想你有理由把这些都记在记忆里。