Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2009.01.25;
Скачать: [xml.tar.bz2];

Вниз

TObjectList. Правильно ли я делаю?   Найти похожие ветки 

 
zorik ©   (2008-12-11 09:58) [0]


TMyList = class;

TMyItem = class
private
 FMyList: TMyList;
 ...
public
 ...
end;

TMyList = class(TObjectList)
private
 ...
public
 function Add(AItem: TMyItem): Integer; overload;
 function Add: Integer; overload;
 property Items[Index: Integer] read GetMyItem write SetMyItem;
end;

implementation

function TMyList.Add(AItem: TMyItem): Integer;
begin
 result := inherited Add(AItem);
end;

function TMyList.Add: Integer;
var
 AItem: TMyItem;
begin
 AItem: TMyItem.Create;
 AItem.FMyList := self;
 result := Add(AItem);
end;


Интересует коректность выделеного с точки зрения ООП. Дело в том что TMyItem нужно знать свойства TMyList


 
Юрий Зотов ©   (2008-12-11 10:48) [1]

Объект получает ссылку на свой контейнер - обычное дело.

А вот насчет Add - здесь можно наступить на грабли. Delphi под рукой нет, поэтому проверьте сами - если TObjectList уже имеет метод Add и этот метод не виртуальный, то свои методы Add назовите как-нибудь иначе (например, AddItem). Вообще, если унаследованныйо метод Add(TObject) уже есть, то метод Add(TMyItem) совсем не нужен.

И подумайте насчет Extract. После вызова этого метода объект уже не будет содержаться в контейнере - поэтому не стоит ли в объекте очистить ссылку на контейнер?


 
Ins ©   (2008-12-11 10:50) [2]

На мой взгляд, ничего противозаконного тут нет. Нормально, что элемент списка знает о своем контейнере, иногда это нужно. А в RTL/VCL и не такое встречается, так порой класс использует в своем интерфейсе своих же потомков (TFiler -> TReader/TWriter), вот это немного диковато для меня :)


 
Сергей М. ©   (2008-12-11 10:51) [3]

Вполне корректно.


 
Сергей М. ©   (2008-12-11 11:00) [4]

Родителем-контейнером вполне можно выбрать и T[Owned]Collection и TComponent - обр.связь элементов с контейнером там уже реализована.


 
Ega23 ©   (2008-12-11 11:01) [5]

Я так потомков обычно делаю:


 TSomeObject = class (...)
 end;

 TChildObjectList = class (TObjectList)
 private
   function GetItem(Index: Integer): TSomeObject;
 public
   function Add(Item : TSomeObject) : Integer;
   property Items[Index : Integer] : TSomeObject read GetItem; default;
 end;

function TChildObjectList.GetItem(Index : Integer): TSomeObject;
begin
 Result := TSomeObject(inherited GetItem(Index));
end;

function TChildObjectList.Add(Item : TSomeObject) : Integer;
begin
 Result := inherited Add(Item);
end;



 
zorik ©   (2008-12-11 11:13) [6]

Хорошо, что нормально

Юрий Зотов ©   (11.12.08 10:48) [1]

function TMyList.Add(AItem: TMyItem): Integer;
begin
result := inherited Add(AItem);
end;

Интересно вызовится ли здесь метод TObjectList.Add()?

И еще меня смущает второй метод Add, где создается AItem:= TMyItem.Create -- я его нигде не уничтожаю. Надо покопать методы Clear и Delete у TObjectList.

Сейчас попробую без переопределения Add(TMyItem).

Extract не использую, но для универсальности надо взять на заметку

И еще подсказочка: для структур типа объекти - линии - точки что лучше использовать? Может покопать в сторону коллекций?


 
Сергей М. ©   (2008-12-11 12:08) [7]


> для структур типа объекти - линии - точки что лучше использовать?
>  Может покопать в сторону коллекций?


Смотря какие операции требуются при работе с этими объектами и контейнерами их содержащими.


 
Ega23 ©   (2008-12-11 12:30) [8]


> И еще меня смущает второй метод Add, где создается AItem:
> = TMyItem.Create -- я его нигде не уничтожаю. Надо покопать
> методы Clear и Delete у TObjectList.


TObjectList - суть такой же контейнер, как и обычный TList. Вся разница в том, что в TList могут храниться любые указатели, в TObjectList - только указатели на объекты.
Соответственно, в случае TList нереально отдать ему (TList) на откуп освобождение памяти, т.к. контейнеру неизвестно, что ты в него запихнул. Поэтому нужно Clear переопределять, что-нибудь в таком духе:


 TGUIDList = class (TList)
 private
   function GetItem(Index: Integer): TGUID;
 protected
   procedure Notify(Ptr: Pointer; Action: TListNotification); override;
 public
   function Add(Item : TGUID) : Integer;
   procedure Clear; override;
   property Items[Index : Integer] : TGUID read GetItem; default;
 end;

function TGUIDList.Add(Item: TGUID): Integer;
var
 p : PGUID;
begin
 New(p);
 p^ := Item;
 Result := inherited Add(p);
end;

procedure TGUIDList.Clear;
var
 i : Integer;
 p : Pointer;
begin
 for i:=0 to Count-1 do
 begin
   p := Get(i);
   Dispose(PGUID(p));
 end;
 inherited;
end;

function TGUIDList.GetItem(Index: Integer): TGUID;
begin
 Result := PGUID(Get(Index))^;
end;

procedure TGUIDList.Notify(Ptr: Pointer; Action: TListNotification);
begin
 inherited;
 if Action in [lnExtracted, lnDeleted] then
   Dispose(PGUID(Ptr));
end;


В случае же TObjectList, поскольку базовый деструктор TObject виртуальный, можно уничтожение объектов смело отдать на откуп контейнеру. А вот отдавать или нет - зависит от тебя. В принципе, в конструкторе TObjectList есть параметр OwnsObjects : Boolean = True. Если он проставлен, то по-умолчанию, при вызове Delete указателя на объект будет вызван деструктор данного объекта (то же относится и к Clear, там все объекты будут убиты). Если ты его поставишь в false, то тогда будет удалён только лишь указатель на объект, а сам объект останется в памяти.

Как-то так.


 
zorik ©   (2008-12-11 12:34) [9]

Ega23 ©   (11.12.08 11:01) [5]

я забрал function Add(Item : TSomeObject) : Integer, так как в таком виде использовать не буду. Оставил:

function Add(const AValue1, AValue2: Integer; . . . );
var
 AItem: TSomeObject;
begin
 AItem := TSomeObject.Create;
 AItem.FValue1 := AValue1;
 AItem.FValue2 := AValue2;
 . . .
 AItem.FMyList := Self;
 Result := inherited Add(AItem);
end;


 
zorik ©   (2008-12-11 12:38) [10]

Ega23 ©   (11.12.08 12:30) [8]

Спасибо. Растолковал. Я как раз перерабатываю старые модули, где через TList было реализовано


 
Ega23 ©   (2008-12-11 12:44) [11]


> я забрал function Add(Item : TSomeObject) : Integer, так
> как в таком виде использовать не буду. Оставил:


Я бы не так сделал. Оставь Add так, как я тебе написал. Дело в том, что в этом случае твой TObjectList пропустит добавление только TSomeObject (или потомков).

Сделай ещё одну функцию, типа:

function AddItemFromValues (const AValue1, AValue2: Integer; . . . ) : Integer;
var
 itm : TSomeObject;
begin
AItem := TSomeObject.Create;
AItem.FValue1 := AValue1;
AItem.FValue2 := AValue2;
. . .
AItem.FMyList := Self;
Result := Add(AItem);
end;


Но если совсем по-правильному делать, то сделай в TSomeObject ещё один альтернативный конструктор:

type
 TSomeObject = class (....)
 public
   constructor Create; overload;
   constructor Create(const AValue1, AValue2: Integer; . . . ); overload;
 end;

constructor TSomeObject.Create;
begin
 inherited Create;
 ......
end;

constructor TSomeObject.Create(const AValue1, AValue2: Integer; . . . );
begin
 Create;
 Value1 := AValue1;
 Value2 := AValue2;
 ........
end;


В этом случае добавление объекта сведётся к строчке:
 TMyObjectList.Add(TSomeObject.Create(1,2));


 
zorik ©   (2008-12-11 12:55) [12]


> Но если совсем по-правильному делать, то сделай в TSomeObject
> ещё один альтернативный конструктор:

Вариант


 
Ega23 ©   (2008-12-11 13:02) [13]


> Вариант


Данный вариант хорош тем, что ты внутри объекта (а не контейнера) можешь проверить валидность твоих const AValue1, AValue2: Integer; . . . . Т.е. если они по каким-то критериям не подходят, то объет либо не создастся (raise exception в конструкторе вызовешь), либо будет как-то "помечен".

Кесарю - кесарево, как говорится.


 
Юрий Зотов ©   (2008-12-11 14:09) [14]


> Ega23 ©   (11.12.08 11:01) [5]


Олег, а если из твоего кода в [5] метод Add просто выкинуть - то что изменится?
:о)

А тогда зачем он нужен?
:о)


 
Ega23 ©   (2008-12-11 14:14) [15]


> Олег, а если из твоего кода в [5] метод Add просто выкинуть
> - то что изменится?


Можно будет в контейнер добавить объект, не являющийся экземпляром TSomeObject или его потомков.

Пример:


 TBusinessAction = class (TObject)
 private
   function GetActionSize : Cardinal;
 public
   property BACode : Integer read FBACode;
   property BAName : string read FBAName;
   property ImgID : Integer read FImgID;
   function SaveToStream(stream : TStream) : Boolean;
   function LoadFromStream(stream : TStream; const Size : Integer) : Boolean;
   procedure LoadFromDataSet(ds : TDataSet);
 end;

 TBusinessActionsList = class (TObjectList)  //!!!!!!
 private
   function GetItem(Index: Integer): TBusinessAction;
 public
   property Items[Index : Integer] : TBusinessAction read GetItem; default;

   function Add(Item : TBusinessAction) : Integer;
   procedure SaveToStream(stream : TStream);
   procedure LoadFromDataSet(ds : TDataSet);
   procedure LoadFromStream(stream : TStream); overload;
   procedure LoadFromStream(stream : TStream; const ItemCount : Integer); overload;
 end;


 
zorik ©   (2008-12-11 15:27) [16]

Ну и еще вопросик: нужен ли метод SetItem?


 
Ega23 ©   (2008-12-11 15:42) [17]


> Ну и еще вопросик: нужен ли метод SetItem?


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

procedure TMyObjectList.SetItem(Index: Integer; const Value: TSomeClass);
var
 itm : TObject;
begin
 itm := Items[Index];
 inherited Items[Index] := Value;
 itm.Free;
end;


 
Юрий Зотов ©   (2008-12-11 18:06) [18]

> Ega23 ©   (11.12.08 14:14) [15]

> Можно будет в контейнер добавить объект, не являющийся экземпляром
> TSomeObject или его потомков.

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

var
 List: TObjectList;
begin
 List := любой_потомок_TObjectList;
 List.Add(любой объект и пофиг твои закрывашки);


 
Ega23 ©   (2008-12-11 18:56) [19]


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


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


 
Юрий Зотов ©   (2008-12-11 19:34) [20]


> Ega23 ©   (11.12.08 18:56) [19]


Все так. Но эта закладка может оказаться бомбой замедленного действия.


 
Ins ©   (2008-12-12 12:11) [21]


> Юрий Зотов ©   (11.12.08 19:34) [20]


А если к списку обратиться через интерфейс TList, а не TObjectList, то приплыли вдвойне, тем не менее, в классе TObjectList методы Add и т.д. переопределены ;-) Так что индульгенция от Борланда получена.

Тут дело не в том, что метод статический, а в том, что у метода при том же имени совсем другой прототип. Можно спорить на тему того, нормально ли  что интерфейс потомка не является надмножеством интерфейса предка. Но язык нам не предоставил других удобных механизмов (до версии 2009, где статический полиморфизм появился в виде дженериков), и приходится делать либо так, либо отказаться от наследования в пользу агрегации.


 
Ins ©   (2008-12-12 12:17) [22]


> Можно спорить на тему того, нормально ли  что интерфейс
> потомка не является надмножеством интерфейса предка.


Хотя не над чем тут спорить, все верно Вы говорите :) Приведение к предку должно ограничивать интерфейс, а не менять его на совсем другой.


 
jack128_   (2008-12-12 13:18) [23]

>
> Его все равно можно добавить. Статический метод нльзя перекрыть,
>  его можно только закрыть (что ты и сделал). Но стоит обратиться
> к твоему списку, как к TObjectList - и ты приплыл. Поэтому
> твой метод ничего не дает.
>
> var
>  List: TObjectList;
> begin
>  List := любой_потомок_TObjectList;
>  List.Add(любой объект и пофиг твои закрывашки);


Статический конструктор нельзя перекрыть, его можно закрыть. Поэтому TComponent.Create(AOwner: TComponent) ничего не дает.


var
 Cls: TClass;
 Obj: TObject;
begin
 Cls := TForm;
 Obj := Cls.Create;
 if Obj is TForm then
   TForm(Obj).Show; // AV
end;


ЗЫ  ИМХО в TMyObjectList перекрыть Notify и поставить там Assert((Item = nil) or (Item is TMyObject)) - и код более менее надежным станет...

PPS конечно мона написать
TMyObjectList = class(TObject)
private
 FItems: TObjectList;
public
 procedure Add(AObj: TMyObject);  
end;

но лениво десяток методов тривиальным писать....


 
Ins ©   (2008-12-12 13:35) [24]


> ЗЫ  ИМХО в TMyObjectList перекрыть Notify и поставить там
> Assert((Item = nil) or (Item is TMyObject)) - и код более
> менее надежным станет...


Да, но лучше бы, чтобы тебе в дизайн-тайм по рукам за такое надавали :)


> но лениво десяток методов тривиальным писать....


Угу. Можно правда попробовать Code Template забубенить :) Да, все равно же никто так делать не станет, все как выводили раньше потомков от TList/TObjectList, так и будут выводить, наверное, пока D2009 не приживется, так как на практике проблема редко возникает, как и с твоим примером с конструктором. Так что стоит ли овчинка...



Страницы: 1 вся ветка

Форум: "Начинающим";
Текущий архив: 2009.01.25;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.006 c
4-1204645261
mephisto
2008-03-04 18:41
2009.01.25
Как узнать заголовок родительского окна процесса


2-1229332381
9899100
2008-12-15 12:13
2009.01.25
Наследник от TGraphicControl


15-1227912693
Petr V. Abramov
2008-11-29 01:51
2009.01.25
Гимн Российской Федерации в исполнении хора мальчиков...


2-1229170431
Klayman
2008-12-13 15:13
2009.01.25
Вылетает при запросах


2-1228923689
Djels
2008-12-10 18:41
2009.01.25
Turbo Pascal





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский