Ориентация объекта и сериализация - PullRequest
4 голосов
/ 05 августа 2009

Рассмотрим интерфейс типа

IMyInterface = interface
  procedure DoSomethingRelevant;
  procedure Load (Stream : TStream);
  procedure Save (Stream : TStream);
end;

и несколько классов, которые реализуют интерфейс:

TImplementingClass1 = class (TInterfacedObject, IMyInterface)
  ...
end;
TImplementingClass2 = class (TInterfacedObject, IMyInterface)
  ...
end;
...

У меня есть класс со списком разработчиков IMyInterface:

TMainClass = class
strict private
  FItems : TList <IMyInterface>;
public
  procedure LoadFromFile (const FileName : String);
  procedure SaveToFile (const FileName : String);
end;

Теперь к вопросу: как я могу загрузить основной класс и особенно список элементов объектно-ориентированным способом? Прежде чем я смогу вызвать виртуальный метод Load для элементов, я должен создать их и, следовательно, знать их тип. В моей текущей реализации я сохраняю количество элементов, а затем для каждого элемента

  • идентификатор типа (IMyInterface получает дополнительную функцию GetID)
  • вызов метода сохранения элемента

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

ID := Reader.ReadInteger;
case ID of
  itClass1 : Item := TImplementingClass1.Create;
  itClass2 : Item := TImplementingClass2.Create;
  ...
end;
Item.Load (Stream);

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

Ответы [ 2 ]

4 голосов
/ 05 августа 2009

Одним из решений было бы создание фабрики, в которой все классы регистрируют себя с уникальным идентификатором.

TCustomClassFactory = class(TObject)
public      
  procedure Register(AClass: TClass; ID: Integer);
  function Create(const ID: Integer): IMyInterface;
end;

TProductionClassFactory = class(TCustomClassFactory)
public
  constructor Create; override;
end;

TTestcase1ClassFactory = class(TCustomClassFactory);
public
  constructor Create; override;
end;

var
  //***** Set to TProductionClassFactory for you production code,
  //      TTestcaseXFactory for testcases or pass a factory to your loader object.
  GlobalClassFactory: TCustomClassFactory;

implementation

constructor TProductionClassFactory.Create;
begin
  inherited Create;
  Register(TMyImplementingClass1, 1);
  Register(TMyImplementingClass2, 2);
end;

constructor TTestcase1ClassFactory.Create;
begin
  inherited Create;
  Register(TMyImplementingClass1, 1);
  Register(TDoesNotImplementIMyInterface, 2);
  Register(TDuplicateID, 1);
  Register(TGap, 4);
  ...
end;

Преимущества

  • Вы можете удалить условную логику из текущего метода загрузки.
  • Одно место, чтобы проверить наличие дубликатов или пропущенных ID.
2 голосов
/ 05 августа 2009

Вам нужен реестр классов, где вы храните каждую ссылку на класс вместе с их уникальным идентификатором. Классы регистрируются в секции инициализации своего модуля.

TImplementingClass1 = class (TInterfacedObject, IMyInterface)
  ...
end;
TImplementingClass2 = class (TInterfacedObject, IMyInterface)
  ...
end;

TMainClass = class
public
  procedure LoadFromFile (const FileName : String);
  procedure SaveToFile (const FileName : String);
end;

Редактировать: перенес реестр классов в отдельный класс:

TMyInterfaceContainer = class 
strict private
class var
  FItems : TList <IMyInterface>;
  FIDs: TList<Integer>;
public
  class procedure RegisterClass(TClass, Integer);
  class function GetMyInterface(ID: Integer): IMyInterface;
end;

procedure TMainClass.LoadFromFile (const FileName : String);
  ...
  ID := Reader.ReadInteger;
  // case ID of
  //   itClass1 : Item := TImplementingClass1.Create;
  //   itClass2 : Item := TImplementingClass2.Create;
  //   ...
  // end;
  Item := TMyInterfaceContainer.GetMyInterface(ID);
  Item.Load (Stream);
  ...

initialization
  TMyInterfaceContainer.RegisterClass(TImplementingClass1, itClass1);
  TMyInterfaceContainer.RegisterClass(TImplementingClass2, itClass2);

Это должно указать вам направление, для хорошего ознакомления с этими методами прочитайте знаменитую статью Мартина Фаулера , особенно раздел о Интерфейс впрыска

...