【问题标题】:delphi prototype pattern德尔福原型模式
【发布时间】:2011-03-30 09:57:52
【问题描述】:

我想知道,Delphi 的 RTTI 中是否有任何东西可以与 C# 中的 MemberwiseClone 一样,用于原型模式的简单实现。 我看到了这种模式的一些 Delphi 实现,其中正在创建一个新对象 (TMyObject.Create),它的属性分配有来自原型对象的值。我可能是错的,但是如果我们以相同的基本方式创建对象,我看不到该模式的好处。

谢谢。

【问题讨论】:

    标签: delphi design-patterns delphi-2007 prototype-pattern


    【解决方案1】:

    Object.MemberwiseClone Method 按照一些非常简单的规则并利用 .NET 垃圾收集器的工作原理,制作对象的浅层副本。

    • 只是简单地复制了引用。这包括字符串和对任何 object 的引用。
    • 值类型是位复制的(生成相同的克隆)。

    关于值类型的部分可以很容易地用 Delphi 复制。用 Delphi 复制引用类型的行为,虽然在技术上很容易,但不会提供预期的结果:Delphi 代码应该.free 它创建的对象,它使用owner-owned 范式来确保发生这种情况。通常的模式是从析构函数中释放所有者对象创建的对象。如果您制作对象的浅拷贝,则会导致失败。这是一个例子:

    • 对象 A 拥有对对象 B 的引用。
    • 我们将对象 C 创建为对象 A 的浅表副本。对象 C 现在包含对对象 B 的引用。
    • 我们释放对象A:A.Free;
    • 我们释放对象 B:B.Free; - 这会自动调用 B.Free,但不幸的是,当我们释放 A 时,B 已经被释放了!

    我们可以尝试 deep-copy,正如 David 建议的那样,但这会带来一些同样困难的问题:

    • 并非所有对象都应该被复制,例如因为它们封装了对现实世界资源的引用(例如:TFileStream)。
    • 其他一些对象不能被深度复制,因为它们本质上是单例。并且没有通用的说法“这个对象是一个单例,做一个普通的引用副本,不要做一个深拷贝”。示例:我们是否复制Application
    • 如果您进行深层复制,您可能会有循环引用,您需要注意这些问题。这不是微不足道的,如果您从集合中的项目开始复制,您可能会发现自己回到集合的父级,即:不完全是预期的结果。
    • 不加选择的深度处理可能会占用意外的内存量并导致意外的内存泄漏。再次考虑集合 -> 项目 -> 复制项目示例,您最终得到了“项目”的副本,但由于意外的反向链接,整个 COLLECTION 都被复制了。

    综上所述,我们只能得出一个结论:我们不能有一个通用的,德尔福等价于MemberwiseClone。对于具有简单交互的简单对象,我们可以部分相似,但这几乎没有那么吸引人!

    【讨论】:

    • 我们假设我们正在讨论原型模式的特定用途,而不是通用的。我们需要深度复制一个自定义但已知的对象。我猜这个模式的目的是加快许多对象的创建,对吧?我们知道这些将是什么物体。考虑到这一切,在 Delphi 中什么是好的解决方案?使用内存流,序列化......?并且不使用原型对象中的已发布属性。
    • @elector,我需要克隆的 Delphi 7 对象实际上提供了一个返回对象副本的 Duplicate 方法; Duplicate 方法通常是手动实现的(逐个字段分配),但我有一些依赖于一些流代码的实现。流式代码本身是手写的(逐个字段),我的Duplicate 因为我的懒惰而只使用流式处理:逐个字段的手动复制会更快。
    • 通过字段分配方法,原型模式有意义吗?我们是否可以更快地创建大量对象?我猜流式代码不会使对象的创建更快?
    • @elector,定义 fast。一旦您编写了克隆代码,就需要更少的代码行来进行克隆;但是 speedprototyping 不能很好地混合:原型代码应该是快速编写的,而不是快速执行的。引发这一切的 .NET 方法不会特别快,因为它很可能使用反射来执行其职责。
    【解决方案2】:

    没有任何内置功能可以为您执行深度克隆。我相信你可以编写一个基于新 RTTI 的深度克隆,但我希望它是一项不平凡的工作。

    如果您正在处理足够简单的类型,它会很好地工作,但您很容易遇到严重的挑战。例如,在我的脑海中:

    • 需要按特定顺序创建某些对象组。
    • 不应克隆类的某些成员,例如参考计数。您如何识别那些患有 RTTI 的人?
    • 如何处理单身人士?
    • 任何需要设置的外部引用怎么办?假设您克隆了一个通常由工厂创建的对象。如果该工厂持有对其创建的对象的引用,那么在其背后使用可能会破坏您的设计。

    您可以通过定义一个基本的Clone() 方法来实现您的原型模式,该方法将RTTI 用于简单类型,然后您必须为更复杂的类型覆盖它。不过就个人而言,我会继承 TPersistent 并基于 Assign 创建我的 Clone() 方法。

    【讨论】:

    • 如果我使用的是 D2007,我该怎么办?我想 Clone() 在 2007 版本中不可用?
    • @elector Clone 是您自己编写的函数。这才是 Prototype 的精髓所在。如果您有更新的 Delphi,那么 RTTI 可能会有所帮助,但在 2007 年您肯定必须自己动手。
    • 或者您建议为 D2007 的 TPersistent 分配?那会是一个很好的原型模式实现示例吗?编辑:我刚刚看到你对我上一个问题的回答。谢谢
    • @elector 我想说TPersistent.Assign 可以用来实现原型。但是,它不是真正的 Prototype,因为它的主要用途是便于在不同但相关类型的对象之间复制数据。
    • 从精益 OOP 的角度来看,我会说如果该工厂持有对其创建的对象的引用,那么它会做“太多”(单一责任原则)——在现实生活中,如果它的实现方式如下这样,它还可以提供一种将这些外部创建的对象添加到引用列表的方法:)
    【解决方案3】:

    有一种方法可以在 Delphi 中执行对象的深拷贝(克隆)。它适用于最新版本的 Delphi(2010 及更高版本)。请参阅下面的代码片段......它实际上非常简单,您不需要外部库。你可以在这里找到更多信息:http://www.yanniel.info/2012/02/deep-copy-clone-object-delphi.html

    function DeepCopy(aValue: TObject): TObject;
    var
      MarshalObj: TJSONMarshal;
      UnMarshalObj: TJSONUnMarshal;
      JSONValue: TJSONValue;
    begin
      Result:= nil;
      MarshalObj := TJSONMarshal.Create;
      UnMarshalObj := TJSONUnMarshal.Create;
      try
        JSONValue := MarshalObj.Marshal(aValue);
        try
          if Assigned(JSONValue) then
            Result:= UnMarshalObj.Unmarshal(JSONValue);
        finally
          JSONValue.Free;
        end;
      finally
        MarshalObj.Free;
        UnMarshalObj.Free;
      end;
    end;
    

    【讨论】:

    • 不错!但是我想如果对象包含一个具有循环对象引用的集合,它将不起作用?
    • 它确实适用于集合。我用一个包含通用 TList 和其他对象字段的对象对其进行了测试。成功了!
    • 这会引发错误“内部:当前不支持类型 tkPointer”,我在 XE 上并且有一个复杂的对象要进行深层复制。
    【解决方案4】:

    我认为您正在寻找类似的东西:http://code.google.com/p/delphilhlplib/source/browse/trunk/Library/src/Extensions/DeHL.Cloning.pas

    它仅适用于 D2010 及更高版本(需要扩展 RTTI)。

    【讨论】:

    • 谢谢,这是一个很好的资源,但我犯了一个错误,没有在我的问题中提到我使用 D2007。
    【解决方案5】:

    我不久前发布了一个有点通用的component cloning 答案,它可能有用,尽管它不等同于MemberWiseClone。我相信它在 Delphi 中可以追溯到 D5,而且我确信它在 D2007 中也可以使用。

    【讨论】:

      猜你喜欢
      • 2014-07-08
      • 1970-01-01
      • 1970-01-01
      • 2013-01-15
      • 2010-12-26
      • 2010-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多