Правильный способ дублирования объекта Delphi - PullRequest
21 голосов
/ 28 октября 2010

Каковы плюсы и минусы дублирования экземпляра объекта с помощью конструктора или функции экземпляра?

Пример 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;

Пример 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;

Одинсразу же возникает важное различие - в последнем случае конструктор Create должен быть виртуальным, чтобы иерархия классов, поддерживающая Clone, могла быть построена на основе TMyObject.

Предположим, что это не проблема - что TMyObject и все, что на нем основано, полностью находится под моим контролем.Какой ваш предпочтительный способ сделать конструктор копирования в Delphi?Какую версию вы считаете более читабельной?Когда бы вы использовали прежний или последний подход?Обсудить.:)

РЕДАКТИРОВАТЬ: Моя основная проблема с первым примером заключается в том, что использование является очень тяжелым по сравнению со вторым подходом, то есть

newObj := TMyObject.Create(oldObj)

против.

newObj := oldObj.Clone;

EDIT2 или «Почему я хочу однострочную операцию»

Я согласен, что Assign является разумным подходом в большинстве случаев.Даже разумно реализовать «конструктор копирования» внутри, просто используя assign.

Обычно я создаю такие копии при многопоточности и передаче объектов через очередь сообщений.Если создание объекта происходит быстро, я обычно передаю копию исходного объекта, потому что это действительно упрощает вопросы владения объектом.

IOW, я предпочитаю писать

Send(TMyObject.Create(obj));

или

Send(obj.Clone);

до

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

Ответы [ 3 ]

27 голосов
/ 28 октября 2010

Первый добавляет информацию о том, какой объект нужно создать, второй нет. Это может быть использовано для создания экземпляра, например потомок или предок класса

Способ Delphi (TPersistent) разделяет создание и клонирование:

dest := TSomeClass.Create; 
dest.Assign(source);  

и обладает тем же свойством, которое вы явно выбираете для создания экземпляра класса. Но вам не нужны два конструктора, один для обычного использования, а другой - для клонирования.

редактировать из-за требования oneline Конечно, вы можете смешивать его, используя метаклассы Delphi (не проверено)

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
      Result := TBaseObjectClass(Self.ClassType).Create;
    Result.Assign(Self);
  end;


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

В остальном, просто реализуйте свои assign() операторы, и вы можете смешивать несколько способов.

edit2

Я заменил код выше на код, протестированный в D2009. Есть некоторые зависимости типов, которые могли бы вас запутать, надеюсь, это будет понятнее. Конечно, вам придется изучить механизм присваивания. Я также протестировал параметр metaclass=nil по умолчанию, и он работает, поэтому я добавил его.

6 голосов
/ 28 октября 2010

Я не думаю, что есть правильный путь, это зависит только от личного стиля.(И, как отметил Марко, есть и другие способы.)

  • Путь конструктора короткий, но он нарушает принцип, согласно которому конструктор должен только конструировать объект.Что, возможно, не является проблемой.
  • Способ клонирования короток, хотя вам необходимо обеспечить вызов для каждого класса.
  • Способ назначения более похож на Delphi.Он разделяет создание и инициализацию, и это хорошо, потому что нам нравится концепция «один метод - одна функция», которая делает код более удобным для поддержки.

И если вы реализуете Assign с использованием потоков, у вас есть только одно место, о котором нужно беспокоиться, какоеполя должны быть доступны.

3 голосов
/ 28 октября 2010

Мне нравится стиль клон - но только в Java (или любом другом языке GC).Я использовал это несколько раз в Delphi, но в основном я остаюсь с Create и Assign, потому что гораздо яснее, кто несет ответственность за разрушение объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...