views:

174

answers:

3

What are pros and cons of duplication an object instance with constructor or instance function?

Example A:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    constructor Create(srcObj: TMyObject); overload; 
    //alternatively:
    //constructor CreateFrom(srcObj: TMyObject);
    property Field: integer read FField;
  end;

constructor TMyObject.Create(srcObj: TMyObject);
begin
  inherited Create;
  FField := srcObj.Field;
end;

Example B:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    function Clone: TMyObject;
    property Field: integer read FField;
  end;

function TMyObject.Clone: TMyObject;
begin
  Result := TMyObject.Create;
  Result.FField := FField;
end;

One major difference immediately springs to mind - in the latter case the Create constructor would have to be virtual so that a class hierarchy supporting Clone could be built basing on the TMyObject.

Assume that this is not a problem - that TMyObject and everything based on it is entirely under my control. What is your preferred way of doing copy constructor in Delphi? Which version do you find more readable? When would you use former or latter approach? Discuss. :)

EDIT: My main concern with the first example is that the usage is very heavy compared to the second approach, i.e.

newObj := TMyObject.Create(oldObj)

vs.

newObj := oldObj.Clone;

EDIT2 or "Why I want single-line operation"

I agree that Assign is a reasonable approach in most cases. It's even reasonable to implement 'copy constructor' internally by simply using assign.

I'm usually creating such copies when multithreading and passing objects through the message queue. If object creation is fast, I usually pass a copy of the original object because that really simplifies the issues of object ownership.

IOW, I prefer to write

Send(TMyObject.Create(obj));

or

Send(obj.Clone);

to

newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
+11  A: 

The first adds information about which object to want to create, the second not. This can be used to instantiate e.g. a descendat or an ancestor of a class

The Delphi way (TPersistent) separates creation and cloning:

  dest:=TSomeClass.create; 
  dest.assign(source);  

and has this same property that you explicitely choose the class to instantiate. But you don't need two constructors, one for normal use, and one where you want to clone.

edit due to oneline requirement You can mix it of course using Delphi metaclasses (untested)

       Type
           TBaseSomeObject= Class;
           TBaseObjectClass = Class Of TBaseSomeObject;

           TBaseSomeObject = class(TPersistent)
                  function clone(t:Tbaseobjectclass=nil):TBaseSomeObject; virtual;
               end;


       function TBaseSomeObject.clone(t:Tbaseobjectclass=nil):TBaseSomeObject;
       begin
          if assigned(t) then
             result:=t.create
          else
            begin
              result:=TBaseObjectClass(self.ClassType).Create;
              result.assign(self);
            end;
       end;


 sendobject(obj.clone); // full clone.
 sendobject(obj.clone(TDescandantObject)); // Cloned into Descendant object 

For the rest, just implement your assign() operators, and you can mix multiple ways.

edit2

I replaced the code above with code tested in D2009. There are some dependancies of the types that might have confused you, hope it is clearer this way. Of course you'll have to study the assign mechanism. I also tested the metaclass=nil default parameter and it works, so I added it.

Marco van de Voort
I would vote for your answer, but this would destroy your 5,555 score. ;-) Congrats!
splash
Well, I do want to reach 6666 one day :-)
Marco van de Voort
OK, here you go! +1
splash
I can't make your metaclass solution work in either 2007 or XE. Can you correct your example so that it would compile?
gabr
All clear now. (Actually, it was all clear before but I was stupidly misreading your code again and again.)
gabr
The biggest problem of course being that you must establish an own root class.
Marco van de Voort
Yes, but as I stated I have complete control over that.
gabr
+2  A: 

I don't think there is a correct way it just depend on personal style. (And as Marco pointed out, there are more ways.

  • The constructor way is short but it violates the principle that the constructor must only construct the object. Which is possibly not a problem.
  • The clone way is short although you need to provide a call for each class.
  • The assign way is more Delphi like. It separates creation and initialization which is good because we like the one method one function concept that makes code better to maintain.

And if you implement Assign using streams, you have only one place to worry about which fields need to be available.

Gamecat
+2  A: 

I like the clone style - but only in Java (or any other GC language). I used it some times in Delphi, but mostly I stay with Create and Assign, because it is much clearer who is responsible for the destruction of the object.

splash
I in general hate using constructors with arguments. When you start making your framework more complicated, it starts biting you in the <censored>
Marco van de Voort
I really like constructors which enforce passing all necessary objects, like `Create(AOwner)`. Dependency injection frameworks know both flavours, Property- or Constructor based dependency injection - both have their pros (and cons).
mjustin
@mjustin: me too, but I usually don't use constructors for creating a copy.
splash