【问题标题】:Different ways to implement 'dirty'-flag functionality [closed]实现“脏”标志功能的不同方法[关闭]
【发布时间】:2010-10-07 22:06:46
【问题描述】:

几乎每个程序员一生都做过一次:如果变量的值发生变化,则设置一些标志。总是有很多属性,如果有变化,你想跟踪一下

  1. 在任何属性中
  2. 在特定属性中
  3. 或在某些属性中

我对在上述情况下实现“脏标志”功能的不同方式感兴趣,除了在每次属性更改时更新标准对象范围的脏标志。肯定有比在每个 setter 中放置 "dirty = true" 更好的方法:它只是看起来很丑,而且是一项乏味的工作。

【问题讨论】:

    标签: design-patterns


    【解决方案1】:

    对于我的 DAO,我保留了从数据库中检索到的原始值的副本。当我发送它进行更新时,我只是将原始值与当前值进行比较。它的处理成本有点高,但比每个属性都有一个脏标志要好得多。

    编辑以进一步证明没有脏标志:如果属性返回其原始值,则无法反映这一点,脏标志继续脏,因为原始值丢失。

    【讨论】:

    • 为什么比为每个属性设置一个脏标志要好得多?您在内存中保留了更多数据。我不是说你所做的并不好,而是你没有证明它的合理性。
    • 如果您真的考虑一下,我不一定会在内存中保留更多数据,因为我不需要为每个属性携带状态标志。如果值恢复为原始值,我无需担心将其设置回“未更改”。脏标志也不允许我回去。
    • 我正在对我的 DAO 做同样的事情。很高兴得到确认!
    • 使用脏标志:1 个具有 {n} 个属性的对象需要一个标志,使用您描述的方式:1 个具有 {n} 个属性的对象需要跟踪 {n} 个属性的初始值。似乎需要大量的工作来存储更多的变量。
    • @Allen - 我跟踪每个属性,因此我不包括在更新语句中更改和恢复的属性。它不是很多工作或很多变量,它是同一对象的单独实例。
    【解决方案2】:

    我曾经有一个基础实体类,提供脏/删除逻辑。

    在编写实体子类时,您可以执行以下操作:

    public string Name
    {
        get { return name; }
        set { setValue("Name", value); }
    }
    

    这很好用,但有“丑陋的弦”病……

    今天你可以使用 Lambda 表达式来排除字符串:

    set {setValue(x => x.Name, value);}
    

    或者,我认为这是最好的解决方案,您可以使用AOP

    https://www.postsharp.net/

    这样,您可以通过属性定义操作。您创建一个属性并指定当用户更改关联的属性时,实体会变脏。

    此外,您可以在您的类(基本实体)中保留一个属性列表,该列表将记住更改的属性,并从您的 AOP 代码访问该列表。

    【讨论】:

    • 这听起来像是 IMO 的最佳方法
    • 投票支持 AOP 建议,但最初的“setValue”方法让我畏缩 :)
    【解决方案3】:

    我创建了一个名为DirtyValue<T> 的类,它有一个原始值和一个当前值。在第一次使用时,它会设置原始值和当前值。连续调用只设置当前值。

    您可以通过比较两者来判断它是否已更改,使用名为 IsDirty() 的只读 bool 属性。使用这种技术,您还可以访问原始值。

    【讨论】:

    • 是 DirtyValue 类型的属性,还是只有 DirtyValue 类型的字段,然后通过属性公开 Currentvalue?仍然看起来很辛苦 - 虽然很好,你不必设置 isDirty = true;到处都是
    • 是的,它们是值而不是属性。如有必要,我会通过道具暴露它们。我想确保无论它们是如何设置的(使用属性或直接设置),肮脏的东西仍然有效。
    • 呃,我的意思是字段,而不是值! :)
    • 更多信息:我实际上有一个 DirtyState 对象,它的作用就像一个 ASp.NET 会话对象,你可以用一个键向它抛出任何脏值,这样你就不必预先声明它们.这恰好解决了我开发的游戏服务器中的一个问题。
    • 所以你的字段不是单独的变量,而是某种属性映射?
    【解决方案4】:

    如果您正在设置“脏”标志,请意识到您正在保持状态。在某些时候,您需要根据该状态采取行动,否则您不需要保留标志。那么问题就变成了:是否有另一种方法来触发所需的操作?发送某种消息?谁使用“脏”状态并采取行动,该通知是否有更简洁的界面?

    【讨论】:

      【解决方案5】:

      在某些具有数据写入任务和独立读取任务的情况下,我为每个任务分配了一个updateCount 变量。生产者每次写入时都会增加其计数。每当阅读器醒来并发现其计数小于生产者的计数时,它都会使用当前值进行更新。您需要对计数器溢出进行一些特殊处理,但这很容易实现。

      我已经成功地在模拟中使用了这种技术 - 其中生产者是物理循环,阅读器是 3d 显示器。

      【讨论】:

        【解决方案6】:

        我会将 changed() 放在每个 setter 中,即调用私有方法而不是仅仅更改标志。然后该方法可以设置一个标志或进行任何需要的处理,例如它也可以通知任何观察者。

        【讨论】:

          【解决方案7】:

          显式dirty=true 方法的一个有趣替代方案是使用保护页,尽管在大多数情况下它可能是过度杀伤并且通常不适用。将内存页面设置为只读(例如,在 Windows 上使用VirtualProtect())并在程序尝试写入页面时捕获信号/异常。记录页面已被修改,然后将页面的保护标志更改为可写并继续执行。

          这是操作系统通常采用的技术,用于确定在将页面从 RAM 中逐出之前是否需要将其写入交换文件。

          【讨论】:

            【解决方案8】:

            您可能希望查看覆盖域对象的 gethashcode 和 equals 方法,并按对象键将原始哈希码存储在哈希表中。然后创建一个接受对象的进程,在哈希表中找到它的键并比较哈希值。

            • 如果哈希相同,则没有变化。 (不要发送到存储库或数据库)
            • 如果散列不同,对象有变化。 (更新)
            • 如果未找到键,则对象是新的。 (插入)
            • 不确定如何确定是否需要删除对象,而不是根据请求处理删除并且不对其使用哈希码跟踪。

            我还没有尝试过,哈希表可能不是跟踪对象键/哈希值的最佳方式。它将通过仅跟踪哈希码和键来节省内存。我不是 100% 确定,但我认为某些 orm 可能会在他们的数据上下文/跟踪对象中使用此方法。

            【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-05-08
            • 2019-10-05
            • 2013-01-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-08
            相关资源
            最近更新 更多