Reputation: 451
i have ObjectList container and i want to add an internal iterator (Visitor Pattern) in fact i'm attempting to determine duplicates in my List..
a sample: http://pastebin.com/pjeWq2uN
this code to provide an insight of what i'm trying to achieve..
TFindDuplicatesMethod = procedure(s1, s2: string) of object;
TPersonList = class(TObjectList)
public
procedure Iterate(pMethode: TFindDuplicatesMethod)
end;
procedure TPersonList.Iterate(pMethode: TFindDuplicatesMethod)
var
i: Integer;
begin
for i := 0 to Count - 1 do
pMethode(TMyClass(Items[i]).S1, {But i don't have the second parameter because
it comes from outside of PersonList Ex: OpenDialog.Files[i]})
end;
function FindDuplicate(S1, S2: string): Boolean;
begin
Result := False;
if S1 = S2 then
Result := True;
end;
begin
Files.Iterate(FindDuplicates(S1, S2));
end;
i'm wondering how OOP solve such problem.
thank's in advance...
Upvotes: 1
Views: 5913
Reputation: 3234
Ok, as we found in comments, we have 2 tasks:
TObjectList
already contains an item (so new item is an duplicate)TImageList
to reduce memory usage and store only unique icons.As I mentioned in comments, you should ask about your second question in separate thread, but I suggest you to add new files icons depeding on new file mime-type, instead of binary icon data. Here you have to create file-type dictionary, determine file-type and so on..
What about duplicates in TObjectList
.
You probably know, that there is generic
implemntation of TObjectList
- TObjectsList<T>
. As in your example you can define TPersonList
as TObjectList<TPerson>
, so items
property always returns TPerson
objects instance.
now, generic task with lists - list sorting. Take a look at Sort()
method of TObjectList<T>/TList
. It has 2 overload methods. One of them is default, and second takes an Comparer
as parameter. Actually, the first method also uses an comparer - default comparer.
So comparer is an implemntation of IComparer<T>
interface wich has the only method - function Compare(l,r : T):integer
; Usually you define this sort-comparer at runtime as anonimous method, before calling the Sort()
method. Using you anonimous method you always know how to compare two T
-typed objects, and then you can determine wich of them is "greater" than other, and should be the first in list.
so the same situation you have while searching for duplicates in list. but now you have to determine, are 2 objects equal or not.
Let us suppose you have personList : TPersonList
wich contains TPerson
instances. Each person has, for exmaple, name, surname, date of birth and ID.
Of course default comparer knows nothing about how to compare 2 persons. But you can provide new comparer wich knows. For example, let suppose 2 objects are equals, if their IDs are equal;
TPerson = class(TObject)
strict private
FName : string;
FSurname : string;
FDateOfBirth : TDateTime;
FId : string; {passport number or something else}
public
constructor Create(aID : string; aDoB : TDateTime);
property Name : string read FName write FName;
property Surname : string read FSurname write FSurname;
property DateOfBirth : TDateTime read FDateOfBirth;
property ID : string read FId;
end;
TPersonList = class(TObjectList<TPerson>)
public
constructor Create();
end;
TPerson
constructor is usual:
constructor TPerson.Create(aID: string; aDoB: TDateTime);
begin
inherited Create();
FID := aId;
FDateOfBirth := aDoB;
end;
now we have to write TPersonList
contructor. As you can see,TObejctList
constructor has few overloads. One of them has Comparer
parameter. It saves aComparer
to FComparer
field. Now, take a look at Contains
method. It finds does list already contain object or not. It uses IndexOf
method. So if returned index = 0 then list contains our object.
So now our task is to define new comparer in TPersonList
constructor. We should define comparsion method, then create comparer object and pass it to List contructor.
constructor TPersonList.Create();
var comparer : IComparer<TPerson>;
comparison : TComparison<TPerson>;
begin
comparison := function(const l,r : TPerson):integer
begin
if l.ID = r.id then exit(0)
else if l.ID > r.ID then exit(-1)
else exit(1);
end;
comparer := TComparer<TPerson>.Construct(comparison);
inherited Create(comparer);
end;
to test our code, lets add some persons to list.
procedure TForm2.FormCreate(Sender: TObject);
var persons : TPersonList;
function AddPerson(id : string; date : TDateTime):boolean;
var p : TPerson;
begin
p := TPerson.Create(id, date);
result := not persons.Contains(p);
if result then
persons.Add(p)
else begin
ShowMessage('list already contains person with id = ' + id);
p.Free();
end;
end;
begin
persons := TPersonList.Create();
try
AddPerson('1', StrToDate('01.01.2000'));
AddPerson('2', StrToDate('01.01.2000'));
AddPerson('3', StrToDate('01.01.2000'));
AddPerson('2', StrToDate('01.01.2000')); // we will get message here.
finally
persons.Free();
end;
end;
So, this is the usual way how to determine if TList
(or its descendant) contains object.
Upvotes: 12