今天看了会刘艺写的<<Delphi面向对象编程思想>>
79页的
示例程序 3-10 Factory Method 模式下动态控件创建的进一步改进
有了点疑惑,代码贴出来,大家有时间看看
unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls;type
  TControlClass = class of TControl;  TControlFactory = class
  private
    FControlObj: TControl;
  public
    constructor Create(AOwner: TWinControl; ControlClass: TControlClass);
  end;  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    RadioGroup1: TRadioGroup;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;var
  Form1: TForm1;
  ControlObj: TObject;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
beginend;{ TControlFactory }constructor TControlFactory.Create(AOwner: TWinControl;
  ControlClass: TControlClass);
begin
  FControlObj := ControlClass.Create(AOwner);
  FControlObj.Parent := AOwner;
  FControlObj.Name := FControlObj.ClassName;
  FControlObj.SetBounds(10, 10, 250, 150);
  Self := TControlFactory(FControlObj);
end;procedure TForm1.Button1Click(Sender: TObject);
const
  ControlClassArray: array[0..2] of TControlClass =
                     (TMonthCalendar, TMemo, TColorBox);
var
  I: Integer;
begin
  for I := 0 to ControlCount - 1 do
    if (Controls[I] is TMonthCalendar) or (Controls[I] is TMemo)
       or (Controls[I] is TColorBox) then
       Controls[I].Free;  ControlObj := TControlFactory.Create(Self,
    ControlClassArray[RadioGroup1.ItemIndex]);
   Label1.Caption := ControlObj.ClassName;  if (ControlObj is TMemo) then TMemo(ControlObj).Lines.Add('测试成功!');
  if (ControlObj is TColorBox) then TColorBox(ControlObj).ItemIndex := 2;
end;end.
疑惑的地方在TControlFactory的Create里
Self := TControlFactory(FControlObj);
我不知道是不是我好久没看delphi的缘故,这句我觉得这样写那TConrolFactory申请的这段内存的引用岂不是丢了
我觉得这产生内存的泄漏,而作者在这写这是比较绝妙的
我天生愚笨,没看懂,大家看一下.

解决方案 »

  1.   

    Self := TControlFactory(FControlObj);//完全正确。
      

  2.   

    不仅丢了,而且找不回来。不懂,为什么要去修改Self,这样有什么好处呢?Delphi又不是Java或者C#,可以自动回收内存,没有Owner的话,根本无法自动管理内存。
    刘艺的口碑并不好,以前写的Midas的书就很烂,不客气的说是误人子弟。
      

  3.   

    书买了还没看,直接问刘艺人家也很少理我(哭....)8过,现在写书的这些人我感觉水平也是高低层次不齐,而且水平高的也经常会在书中发生错误!比如乔林的《参透Delphi/Kylix》里面就有一个大错误!
      

  4.   

    不明白啊。首先我觉得这没什么“绝妙”的,不就是使用类引用吗?再说了,类工厂也不应该这样来用吧,怎么用TControlFactory本身的构造函数来创建需要的实例呢?因为类工厂本身也应该是一个对象,它这样子一弄,类工厂本身就没有了。为什么不使用类方法呢?这样也无需创建类工厂实例就可以制造对象。————————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    ————————————————————————————————————
      

  5.   

    是啊,我也感觉没什么绝妙的
    他的第一个例子是用的类方法
    这是第二个例子
    书上的原话:
    '更绝妙的是通过一条转型赋值语句,将动态创建的控件对象FContorlObj向上转型为
    TControlFactory类型,并赋值给TControlFactory自身,由TControlFactory来传递,
    这就使得TControlFactory有了七十二变的本事';
    我看的晕晕乎乎的
      

  6.   

    给读者的回复:
    ----------------------------------------------
    1、讲的对,示例程序3-10存在TControlFactory实例无法销毁,内存泄漏问题。
    但TControlFactory的功能是创建其他实例(FControlObj),它所保存的唯一变量(FControlObj)只是一个实例的引用,不需要分配多少内存。所以可以不用考虑内存回收问题。
    2、结合本书该部分的上下文,可以明显看出示例程序3-10的主要目的是来讲解和模拟构造函数的行为。
    ControlObj:=TControlFactory.create(self,
                    ContrlClassArry[RadioGroup1.ItemIndex]);也是为了演示TControlFactory的构造函数不是返回它自己的实例,而是摇身一变返回动态创建控件的实例。这种打破常规的例子是为了加深读者对构造函数、self参数、转型赋值的理解,启发思路的意义甚于实用的意义。所谓“绝妙”是指这个例子产生的效果(构造函数返回的不是它自己的实例,而可以动态构造出一个任意的实例,即书中所说的“七十二变”。),而不是指程序本身。
    严格地讲,示例程序3-10还不是一个真正的工厂方法模式,真正的工厂方法模式是前一例(示例程序3-9)
    3、题外话:鉴于.net是有自动回收内存机制的,以后Delphi.net版本是不是不需要考虑内存回收问题了?看来自动回收内存机制是趋势。------------------------------------------------------------------------------
    感谢xzgyb (老达摩)认真阅读拙作,提出这个问题。
    感谢FrameSniper今天专门告诉我大家在这里提出疑问。 
    欢迎大家继续把发现的问题告诉我,以便以后再版时修订。
                                      刘艺
                                    www.liu-yi.net
      

  7.   


    但TControlFactory的功能是创建其他实例(FControlObj),它所保存的唯一变量(FControlObj)只是一个实例的引用,不需要分配多少内存。所以可以不用考虑内存回收问题。
    /////////
    这句话并没有正真的解决上边的疑惑,类工厂本身也只是一个类,用来创建其它实例,但是,以这这个例子的方法进行下去的话,感觉有些事倍功半,而且本身就已经有违于模式理念;
      

  8.   

    to:newdream(newdream)我一再仔细的看了这段程序,大概的明白了程序的用意。不过首先我觉得这代码至少不应该放在工厂模式进行讨论。而且我觉得这个技巧除了产生了一段悬空的内存外,没有任何意义。反倒比直接使用类型麻烦,工厂的意义就是要隐藏实现,而这段代码已经知道了所有的实现:
    ControlClassArray: array[0..2] of TControlClass =
                         (TMonthCalendar, TMemo, TColorBox);
    也就是可以直接使用 controlobj := ControlClassArray[i].Create(nil);至于作者认为其72变,可能是一时忘记了,delphi的所有类变量都是引用吧,既然是引用,当然可以使她指向任意的内存空间,所以这番手脚和下面代码无异
    TControlFactroy controlfc;
    controlfc 和 controlfc.self是相同的引用,改变self就等于改变了controlfc的引用地址啊
    那么 self := TControlFactroy(...) 等同于 controlfc = TControlFactroy(...)换句话说,作者只是将类型转换放在其他地方做了而已。而这样做的代价就是引起了悬空的内存。代码的不严谨,和现在软件要求的便于理解背道而驰。另外,我认为写书和论坛是两回事,论坛可以随心所欲的发表自己的言论,大家来讨论。而写书必须承担责任,因为很多人会花费金钱和时间来读这本书。不成熟的、想当然的做法真的会误人子弟!这是我个人的想法,如果作者觉得我理解错误还请指出。
      

  9.   

    如果说,只有一点点内存造成泄漏,无所谓,那么我觉得太不严谨了。学术上应该讲究严谨。
    其实,这种情况下,我宁可在TControlFactory中多定义一个ControlObj的属性,然后多加一点代码,就可以了。
    ControlObj:=TControlFactory.create(self, ContrlClassArry[RadioGroup1.ItemIndex]).ControlObj;  //当然这样还是没有保存TControlFactory对象的实例此外,即便.net有GC了,但是为了效率问题,有时还是需要自己手工释放。所以.net中还是提供了IDisposable这个接口,还有using语句,专门用于手工释放资源。
      

  10.   

    我认为内存的泄漏和回收问题,无论提问的还是回答都很明白,无需旁人指点。何况作者在这本书中有专门的章节不厌其烦地讨论了这个问题。
    如果不是孤立地看这段代码,而是联系书中这段上下文去理解的话,不难发现作者此处的良苦用心。其实,示例程序3-9就是一个无可挑剔的程序,很规范的工厂方法模式。远比chechy的解决方法好。可是作者又为何要冒内存泄漏的风险画蛇添足呢?(作者肯定知道会有内存泄漏。这一点我不怀疑)。我想无非是欲通过打破常规的思路演示修改构造函数的奇特效果。这一点在书中示例程序3-10之前就有详细的交代和铺垫。
    当然,删掉这段代码对作者来说是很容易的。不过,至少我自己认为通过这段代码我理解了构造函数和self的作用,明白了构造函数隐式返回了self(这就是为什么叫构造函数,而不是叫构造方法)。以前,我编程很少敢在构造函数中作手脚,都是保守的写法TXXX.create;现在至少我会乐于做这方面的尝试。