Recent

Author Topic: Troubles Implementing Collections / Aggregation in Lazarus  (Read 2928 times)

speleomania

  • New member
  • *
  • Posts: 8
Troubles Implementing Collections / Aggregation in Lazarus
« on: January 26, 2017, 09:46:17 pm »
Hi everyone

This would be my first post here
I've done my share of coding in Pascal/Delphi 10-15 years ago but haven't touched it for the last 10 years
Trying to code a little app now in Lazarus but having issues implementing the nested core structure that would be the engine for my app.
Here's what I'm trying to do and need help with:
At the top level I'll be having a TCity class containing a number of TBuilding classes which in turn need to be able to host a number of TRoom classes. /which may have a number of TPerson classes and so on...you get the idea/
I was thinking of using TCollection and TCollectionItem classes to derive my custom classes from even though I don't really care about the visual functionality and this point so I guess TObjectList would work as well.

To complicate things a little further - I'd like once the framework is in place to be able to extend any of the classes to provide additional functionality without having to modify the original classes. For example the framework should be able to work without issues with TTownHouse objects as long as they're based off the TBuilding class or TConferenceRoom if based on TRoom. Pls note that I don't need to be able to handle multiple types at once...ie all instances will be either TRoom or TConferenceRoom but not both.

This is what I have so far, which unfortunately doesn't quite work:
TRoom = class(TCollectionItem)
RoomNo: integer;
Color: TColor;
end;
TRooms = class(TCollection)
Name: string;
end;
TBuilding = class(TCollectionItem)
Name: string;
Rooms: TRooms;
end;
TBuildings = class(TCollection)
Name:string;
end;

TCity = class(TCollection)
Name: string;
Buildings: TBuildings;
end;

I'm able to do
City: TCity;

City := TCity.Create;
City.Buildings.Create(TBuilding);
// This should be able to work with City.Buildings.Create(TTownHouse);

But things brake down at the third level, while I'm trying to put some rooms in my buildings:
City.Buildings[0].Rooms.Create;
City.Buildings[0].Room[0].Number := 101;

Any hints/suggestions?
I'm sure similar topics have come up before so if anyone can share a link to a similar topic on the net id very much appreciate that as well

Cheers

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7508
Re: Troubles Implementing Collections / Aggregation in Lazarus
« Reply #1 on: January 26, 2017, 09:53:15 pm »

City := TCity.Create;
City.Buildings.Create(TBuilding);

This is wrong. Note that you specify the TYPE of the class to create in the first line, but not in the second.

The second construct, while legal, is for special cases.

You need to create the Buildings collection and tell it it is a collection of  TBuildings

Code: Pascal  [Select]
  1. City.Buildings:=TBuildings.Create(nil,TBuilding);
  2. building1:=TBuilding(City.Buildings.add);

Probably you want to put the above fragment in a method of city.
Note that the current trunk contains code for a generics version of TCollection which is simpler in use.

speleomania

  • New member
  • *
  • Posts: 8
Re: Troubles Implementing Collections / Aggregation in Lazarus
« Reply #2 on: January 26, 2017, 11:17:42 pm »
Thanks for the reply!

I'm afraid this is a bit over my head at this point:
* Which unit do i need to include to be able to use the TGenericColletion type?

* Using the TCollection FPC is not happy with this line:
//  City.Buildings:=TBuildings.Create(nil,TBuilding);  wrong no of params

* How do I go about using/accessing the third and subsequent levels: rooms, persons?
  City := TCity.Create(TBuilding);
  City.Buildings:=TBuildings.Create(TBuilding);

  bld1:=TBuilding(City.Buildings.Add);
  bld1.Name:='Oceanic';

  bld1.Rooms := TRooms.Create(TRoom);

  room1 := TRoom(bld1.Rooms.Add);
  room1.RoomNo:=101; 

...end so on...

I'd like to be able to do

City.Buildings[0].Rooms[0].People[0].Name := 'Tom';



molly

  • Hero Member
  • *****
  • Posts: 2345
Re: Troubles Implementing Collections / Aggregation in Lazarus
« Reply #3 on: January 26, 2017, 11:23:56 pm »
You might want to have a look at this wiki page.

You also might want to add a constructor and destructor for your classes TCity and TRoom that automatically creates and destroy the (sub)containers Buildings and Rooms for you.

Even though the article does not show you the nested functionality that you seek, the basics are the same, you just splitted it up into two container objects.

Your final main part of the code could then look something like this:
Code: Pascal  [Select]
  1. //
  2. //          main
  3. //
  4.  
  5. var
  6.   City      : TCity;
  7.   ABuilding : TBuilding;
  8.   ARoom     : TRoom;
  9.   i         : integer;
  10. begin
  11.   City := TCity.Create;
  12.   City.Name := 'towny';
  13.  
  14.   City.Buildings.Name := 'List of Downtown buildings of city with name City';
  15.  
  16.   // Add a building
  17.   ABuilding := City.Buildings.Add;
  18.   ABuilding.Name := 'Oval Office';
  19.  
  20.   // Add a room to this building
  21.   ARoom := ABuilding.Rooms.Add;
  22.   ARoom.RoomNo := 1;
  23.   ARoom.Color := 100;
  24.  
  25.   // Add another room, this time with RoomNo and color at once
  26.   ARoom := ABuilding.Rooms.Add(2, 200);
  27.   WriteLn('just added a room with room number = ', ARoom.RoomNo, ' and color = ', ARoom.Color);
  28.  
  29.  
  30.   // Add Anoter building, this time with name already supplied
  31.   ABuilding := City.Buildings.Add('The Zoo');
  32.   WriteLn('just added a building with name = ', ABuilding.Name);
  33.  
  34.   // Add a room for this building
  35.   ABuilding.Rooms.Add(24, 300);
  36.  
  37.  
  38.   // do a traversion:
  39.   WriteLn('City :', City.Name);
  40.   WriteLn('Number of Buildings : ', City.Buildings.Count);
  41.   for i := 0 to Pred(City.Buildings.Count) do
  42.   begin
  43.     WriteLn('Building ', Succ(i), ' has ', City.Buildings[i].Rooms.Count, ' rooms');
  44.   end;
  45.  
  46.   City.Free;
  47. end.
  48.  

PS: although perfectly valid, using plain class fields to access variables (personally) gives me te creeps. Usually those are declared as private fields and accessed (read /write or whatever floats your boat) using a (public/published) property.
« Last Edit: January 26, 2017, 11:35:17 pm by molly »

speleomania

  • New member
  • *
  • Posts: 8
Re: Troubles Implementing Collections / Aggregation in Lazarus
« Reply #4 on: January 27, 2017, 12:21:17 am »
Thanks Molly,

The level of nested objects/collections however appears to be a problem as Im unable to do
City.Buildings[0].Rooms[0].People[0].Name := 'Tom';

Issue 1: Not sure how to set Items to be a default property for the collections so that I don't have to type City.Buildings.Items[0]. ...

Iddue 2: Strangely  City.Buildings.Items[0].ClassName returns TBuilding however I'm getting an error "identifier idents no member "Rooms"' as soon as I try to access the next level:
city.Buildings.Items[0].Rooms.Items[0].ClassName     

speleomania

  • New member
  • *
  • Posts: 8
Re: Troubles Implementing Collections / Aggregation in Lazarus
« Reply #5 on: January 27, 2017, 01:24:50 am »
I think I solvd my issues from the previous post:

TBuildings = class(TCollection)
private
  procedure setItem(Index: Integer; AValue: TBuilding);
  function getItem(Index: integer): TBuilding;
public
Name:string;
property Items[Index: Integer]: TBuilding read getItem write setItem; default;
end; 

procedure TBuildings.setItem(Index: Integer; AValue: TBuilding);
begin
  Items[Index].Assign(AValue);
end;

function TBuildings.getItem(Index: integer): TBuilding;
begin
  Result := TBuilding(inherited Items[Index]);
end;   

and implementing a similar set of functions for TRooms as well

Now Im able to do
City.Buildings[0].Rooms[0].RoomNo:=101;
without problems




molly

  • Hero Member
  • *****
  • Posts: 2345
Re: Troubles Implementing Collections / Aggregation in Lazarus
« Reply #6 on: January 27, 2017, 09:25:29 am »
I think I solvd my issues from the previous post:
I suspect that you might be right there

Congratulations on having solved your own question(s)  :D