【发布时间】:2010-10-07 22:06:46
【问题描述】:
几乎每个程序员一生都做过一次:如果变量的值发生变化,则设置一些标志。总是有很多属性,如果有变化,你想跟踪一下
- 在任何属性中
- 在特定属性中
- 或在某些属性中
我对在上述情况下实现“脏标志”功能的不同方式感兴趣,除了在每次属性更改时更新标准对象范围的脏标志。肯定有比在每个 setter 中放置 "dirty = true" 更好的方法:它只是看起来很丑,而且是一项乏味的工作。
【问题讨论】:
标签: design-patterns
几乎每个程序员一生都做过一次:如果变量的值发生变化,则设置一些标志。总是有很多属性,如果有变化,你想跟踪一下
我对在上述情况下实现“脏标志”功能的不同方式感兴趣,除了在每次属性更改时更新标准对象范围的脏标志。肯定有比在每个 setter 中放置 "dirty = true" 更好的方法:它只是看起来很丑,而且是一项乏味的工作。
【问题讨论】:
标签: design-patterns
对于我的 DAO,我保留了从数据库中检索到的原始值的副本。当我发送它进行更新时,我只是将原始值与当前值进行比较。它的处理成本有点高,但比每个属性都有一个脏标志要好得多。
编辑以进一步证明没有脏标志:如果属性返回其原始值,则无法反映这一点,脏标志继续脏,因为原始值丢失。
【讨论】:
我曾经有一个基础实体类,提供脏/删除逻辑。
在编写实体子类时,您可以执行以下操作:
public string Name
{
get { return name; }
set { setValue("Name", value); }
}
这很好用,但有“丑陋的弦”病……
今天你可以使用 Lambda 表达式来排除字符串:
set {setValue(x => x.Name, value);}
或者,我认为这是最好的解决方案,您可以使用AOP:
这样,您可以通过属性定义操作。您创建一个属性并指定当用户更改关联的属性时,实体会变脏。
此外,您可以在您的类(基本实体)中保留一个属性列表,该列表将记住更改的属性,并从您的 AOP 代码访问该列表。
【讨论】:
我创建了一个名为DirtyValue<T> 的类,它有一个原始值和一个当前值。在第一次使用时,它会设置原始值和当前值。连续调用只设置当前值。
您可以通过比较两者来判断它是否已更改,使用名为 IsDirty() 的只读 bool 属性。使用这种技术,您还可以访问原始值。
【讨论】:
如果您正在设置“脏”标志,请意识到您正在保持状态。在某些时候,您需要根据该状态采取行动,否则您不需要保留标志。那么问题就变成了:是否有另一种方法来触发所需的操作?发送某种消息?谁使用“脏”状态并采取行动,该通知是否有更简洁的界面?
【讨论】:
在某些具有数据写入任务和独立读取任务的情况下,我为每个任务分配了一个updateCount 变量。生产者每次写入时都会增加其计数。每当阅读器醒来并发现其计数小于生产者的计数时,它都会使用当前值进行更新。您需要对计数器溢出进行一些特殊处理,但这很容易实现。
我已经成功地在模拟中使用了这种技术 - 其中生产者是物理循环,阅读器是 3d 显示器。
【讨论】:
我会将 changed() 放在每个 setter 中,即调用私有方法而不是仅仅更改标志。然后该方法可以设置一个标志或进行任何需要的处理,例如它也可以通知任何观察者。
【讨论】:
显式dirty=true 方法的一个有趣替代方案是使用保护页,尽管在大多数情况下它可能是过度杀伤并且通常不适用。将内存页面设置为只读(例如,在 Windows 上使用VirtualProtect())并在程序尝试写入页面时捕获信号/异常。记录页面已被修改,然后将页面的保护标志更改为可写并继续执行。
这是操作系统通常采用的技术,用于确定在将页面从 RAM 中逐出之前是否需要将其写入交换文件。
【讨论】:
您可能希望查看覆盖域对象的 gethashcode 和 equals 方法,并按对象键将原始哈希码存储在哈希表中。然后创建一个接受对象的进程,在哈希表中找到它的键并比较哈希值。
我还没有尝试过,哈希表可能不是跟踪对象键/哈希值的最佳方式。它将通过仅跟踪哈希码和键来节省内存。我不是 100% 确定,但我认为某些 orm 可能会在他们的数据上下文/跟踪对象中使用此方法。
【讨论】: