![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
TObjectList. Правильно ли я делаю?
zorik © (2008-12-11 09:58:00)
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
Ins (2008-12-12 13:35:00) [1]
> ЗЫ ИМХО в TMyObjectList перекрыть Notify и поставить там
> Assert((Item = nil) or (Item is TMyObject)) - и код более
> менее надежным станет...
Да, но лучше бы, чтобы тебе в дизайн-тайм по рукам за такое надавали :)
> но лениво десяток методов тривиальным писать....
Угу. Можно правда попробовать Code Template забубенить :) Да, все равно же никто так делать не станет, все как выводили раньше потомков от TList/TObjectList, так и будут выводить, наверное, пока D2009 не приживется, так как на практике проблема редко возникает, как и с твоим примером с конструктором. Так что стоит ли овчинка...
jack128_ (2008-12-12 13:18:00) [2]>
> Его все равно можно добавить. Статический метод нльзя перекрыть,
> его можно только закрыть (что ты и сделал). Но стоит обратиться
> к твоему списку, как к 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 12:17:00) [3]
> Можно спорить на тему того, нормально ли что интерфейс
> потомка не является надмножеством интерфейса предка.
Хотя не над чем тут спорить, все верно Вы говорите :) Приведение к предку должно ограничивать интерфейс, а не менять его на совсем другой.
Ins (2008-12-12 12:11:00) [4]
> Юрий Зотов © (11.12.08 19:34) [20]
А если к списку обратиться через интерфейс TList, а не TObjectList, то приплыли вдвойне, тем не менее, в классе TObjectList методы Add и т.д. переопределены ;-) Так что индульгенция от Борланда получена.
Тут дело не в том, что метод статический, а в том, что у метода при том же имени совсем другой прототип. Можно спорить на тему того, нормально ли что интерфейс потомка не является надмножеством интерфейса предка. Но язык нам не предоставил других удобных механизмов (до версии 2009, где статический полиморфизм появился в виде дженериков), и приходится делать либо так, либо отказаться от наследования в пользу агрегации.
Юрий Зотов (2008-12-11 19:34:00) [5]
> Ega23 © (11.12.08 18:56) [19]
Все так. Но эта закладка может оказаться бомбой замедленного действия.
Ega23 (2008-12-11 18:56:00) [6]
> Его все равно можно добавить. Статический метод нльзя перекрыть,
> его можно только закрыть (что ты и сделал). Но стоит обратиться
> к твоему списку, как к TObjectList - и ты приплыл. Поэтому
> твой метод ничего не дает.
Ну, в таком случае, считай, что это для личного удобства сделано. Чтобы компилятор сразу ошибку мне выдавал, если я пытаюсь туда что-то не то вставить.
Юрий Зотов (2008-12-11 18:06:00) [7]> Ega23 © (11.12.08 14:14) [15]
> Можно будет в контейнер добавить объект, не являющийся экземпляром
> TSomeObject или его потомков.
Его все равно можно добавить. Статический метод нльзя перекрыть, его можно только закрыть (что ты и сделал). Но стоит обратиться к твоему списку, как к TObjectList - и ты приплыл. Поэтому твой метод ничего не дает.
var
List: TObjectList;
begin
List := любой_потомок_TObjectList;
List.Add(любой объект и пофиг твои закрывашки);
Ega23 (2008-12-11 15:42:00) [8]
> Ну и еще вопросик: нужен ли метод 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;
zorik (2008-12-11 15:27:00) [9]Ну и еще вопросик: нужен ли метод
SetItem?
Ega23 (2008-12-11 14:14:00) [10]
> Олег, а если из твоего кода в [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;
Юрий Зотов (2008-12-11 14:09:00) [11]
> Ega23 © (11.12.08 11:01) [5]
Олег, а если из твоего кода в [5] метод Add просто выкинуть - то что изменится?
:о)
А тогда зачем он нужен?
:о)
Ega23 (2008-12-11 13:02:00) [12]
> Вариант
Данный вариант хорош тем, что ты внутри объекта (а не контейнера) можешь проверить валидность твоихconst AValue1, AValue2: Integer; . . .. Т.е. если они по каким-то критериям не подходят, то объет либо не создастся (raise exception в конструкторе вызовешь), либо будет как-то "помечен".
Кесарю - кесарево, как говорится.
zorik (2008-12-11 12:55:00) [13]
> Но если совсем по-правильному делать, то сделай в TSomeObject
> ещё один альтернативный конструктор:
Вариант
Ega23 (2008-12-11 12:44:00) [14]
> я забрал 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:38:00) [15]Ega23 © (11.12.08 12:30) [8]
Спасибо. Растолковал. Я как раз перерабатываю старые модули, где через TList было реализовано
zorik (2008-12-11 12:34:00) [16]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;
Ega23 (2008-12-11 12:30:00) [17]
> И еще меня смущает второй метод 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, то тогда будет удалён только лишь указатель на объект, а сам объект останется в памяти.
Как-то так.
Сергей М. (2008-12-11 12:08:00) [18]
> для структур типа объекти - линии - точки что лучше использовать?
> Может покопать в сторону коллекций?
Смотря какие операции требуются при работе с этими объектами и контейнерами их содержащими.
zorik (2008-12-11 11:13:00) [19]Хорошо, что нормально
Юрий Зотов © (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 не использую, но для универсальности надо взять на заметку
И еще подсказочка: для структур типа объекти - линии - точки что лучше использовать? Может покопать в сторону коллекций?
Ega23 (2008-12-11 11:01:00) [20]Я так потомков обычно делаю:
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;