Recent

Author Topic: Need help with Objects/Classes and lists of Objects/Classes  (Read 436 times)

JoeJoeTV

  • New Member
  • *
  • Posts: 40
Need help with Objects/Classes and lists of Objects/Classes
« on: October 03, 2019, 01:53:35 pm »
Hello there,
I am curreently trying to "modularize" my code.
I have made classes for certain things like tcp server stuff and console in-/output.

So, now I want to do something like "modules":
I have a main unit and a list of "modules", which are objects or classes(I don't know what fits better). These objects/classes are declared in a unit each and inherit from a base object/class TModule. These modules do something different each(a different part of my program).

I also have handlers, which are small objects, that have a function and a string. They get called when a specific event happens and the string matches the incoming data/message.

In the "init" part of my program i want to "register" the "modules", so an instance gets created and put in a list, so when the program can call the update procedures and so on in the "modules".

Here is the code that describes the basic structure i was going for.

Code: Pascal  [Select]
  1. unit unitUtils;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.       Classes, SysUtils, lNet, lnetbase, Video, Keyboard, dateutils, dwsJSON, RegExpr, math, unitConsoleUtils, unitVideoUtils;
  9.  
  10. type
  11.   //Data Storage Types
  12.   TInputCommand = record
  13.     Command: String;
  14.     ArgString: String;
  15.     HasArguments: Boolean;
  16.         end;
  17.  
  18.   TInputData = record
  19.     InputString: String;
  20.     InputBuffer: Array of String;
  21.     InputBufferIndex: Integer;
  22.     InputBufferSelected: Boolean;
  23.   end;
  24.  
  25.   //Handlers
  26.  
  27.   //TConsoleCommandHandler
  28.   // Handles console command "Command" with function "handleCommand"
  29.   TConsoleCommandHandler = Object
  30.     public
  31.       Command: String;
  32.       procedure handleCommand(argString: String);
  33.         end;
  34.  
  35.   //TProtocolCommandHandler
  36.   // handles incoming messages with command "Command" with function "handleCommand"
  37.   TProtocolCommandHandler = Object
  38.     public
  39.       Command: String;
  40.       procedure handleCommand({TODO: JSON OBJECT});
  41.         end;
  42.  
  43.   //TProtocolStatusHandler
  44.   // handles incoming messages with status "Status" with function "handleStatus"
  45.   TProtocolStatusHandler = Object
  46.     public
  47.       Status: String;
  48.       procedure handleStatus({TODO: JSON OBJECT});
  49.         end;
  50.  
  51.   //Main Classes
  52.  
  53.   //TServer
  54.   // Main class for network related stuff
  55.   TServer = class
  56.     private
  57.       tcpServer: TLTcp;
  58.  
  59.       ListenAddress: String;
  60.       ListenPort: Integer;
  61.  
  62.       procedure tcpOnAccept(aSocket: TLSocket);
  63.       procedure tcpOnConnect(aSocket: TLSocket);
  64.       procedure tcpOnError(const msg: String; aSocket: TLSocket);
  65.       procedure tcpOnReceive(aSocket: TLSocket);
  66.       procedure tcpOnDisconnect(aSocket: TLSocket);
  67.       procedure tcpOnCanSend(aSocket: TLSocket);
  68.  
  69.       procedure handleMessage(inputMessage: String); //search for matching handle and use it
  70.  
  71.       //TODO: Protocol Handler Array
  72.       // List with handlers to call when matching message is received
  73.     public
  74.       function startListening(Address: String; Port: Integer): Boolean; //start the tcp server
  75.       function stopListening: Boolean;
  76.  
  77.       procedure Init;
  78.       procedure Update;
  79.       procedure Exit;
  80.  
  81.       function registerProtocolCommandHandler(handler: TProtocolCommandHandler): Boolean; //register handle, so when a matching message is received it is handled by the associated handle
  82.       function registerProtocolStatusHandler(handler: TProtocolStatusHandler): Boolean; //register handle, so when a matching message is received it is handled by the associated handle
  83.  
  84.       constructor Create;
  85.       destructor Destroy; override;
  86.         end;
  87.  
  88.   //TConsole
  89.   // handles input/output stuff for command line
  90.   TConsole = class
  91.     private
  92.       //Input
  93.       InputData: TInputData;
  94.  
  95.       //Output
  96.       outputLog: TOutputLog;
  97.  
  98.       //Functions
  99.       procedure Draw;
  100.  
  101.       function checkCommandString(cmdString: String): TInputCommand;
  102.       procedure handleConsoleCommand(InputString: String);  
  103.       procedure getKeyboardInput;
  104.  
  105.       //TODO: Command Handler Array  
  106.       // List with handlers to call when matching command is received
  107.     public
  108.       Capabilities: TVideoCapabilities;
  109.                        
  110.       procedure Init;
  111.       procedure Update;
  112.       procedure UpdateDrawInfo;
  113.       procedure Exit;
  114.  
  115.       procedure sendConsoleCommand(cmdString: String); //send command to console
  116.       procedure writeLogMessage(Message: String; foreColor: Byte = White; backColor: Byte = Black; prefixString: String = 'MAIN/INFO'); //write message to log
  117.  
  118.       function registerConsoleCommandHandler(handler: TConsoleCommandHandler): Boolean; //register handle, so when a matching console command is received it is handled by the associated handle
  119.  
  120.       constructor Create;
  121.       destructor Destroy; override;
  122.         end;
  123.  
  124.   //TModule
  125.   // class to be inherited
  126.   TModule = Object
  127.     public
  128.       procedure Init;
  129.       procedure Update;
  130.       procedure Exit;
  131.  
  132.       //TODO: Handlers
  133.  
  134.       constructor Create;
  135.       destructor Destroy; override;
  136.         end;
  137.  
  138. const
  139.   olEmptyLogMessage: TLogMessage = (Text: ''; ForeColor: White; BackColor: Black; TimeStamp: (Time: 0; Date: 0); Prefix: '');
  140.   defaultPort: Integer = 1337;
  141.   defaultAddress: String = LADDR_ANY;
  142.  
  143. var
  144.   Console: TConsole;
  145.   Server: TServer;
  146.  
  147. implementation
  148.  
  149. end.
  150.  

So my questions are:
  -Should I use objects or classes for the "modules"
  -How can I make a list of the modules
  -Are the "handlers" a good way to do a callback-sort-of thing or are there better methods for doing this

I hope someone can help me.

Thanks in advance for helping me :D
Lazarus 2.0.4 / FPC 3.0.4 / 32+64bit / Windows 10

JoeJoeTV

  • New Member
  • *
  • Posts: 40
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #1 on: October 09, 2019, 05:16:51 pm »
Can noone help me with this, or did I specify my question wrong?
Lazarus 2.0.4 / FPC 3.0.4 / 32+64bit / Windows 10

Thaddy

  • Hero Member
  • *****
  • Posts: 8898
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #2 on: October 09, 2019, 07:44:00 pm »
Mixing classes with objects is an advanced subject.
You use both, but you seem not to understand the consequences of that.
Although such a model is feasible  you run a high risk of being ignored, simply because there is too much to explain to get it working.
I will have a look tomorrow,
Most people that want to use threading should learn to patch their jeans first: use a needle.

JoeJoeTV

  • New Member
  • *
  • Posts: 40
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #3 on: October 09, 2019, 07:50:04 pm »
Mixing classes with objects is an advanced subject.
You use both, but you seem not to understand the consequences of that.
Although such a model is feasible  you run a high risk of being ignored, simply because there is too much to explain to get it working.
I will have a look tomorrow,

Thanks, I know that this is an advanced subject, but I don't know how else to do it. The core subject is, that I want to modularize my code. Maybe I should have chosen another post title. If you have an idea on how to do this differently, please tell me.
Lazarus 2.0.4 / FPC 3.0.4 / 32+64bit / Windows 10

howardpc

  • Hero Member
  • *****
  • Posts: 3148
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #4 on: October 09, 2019, 11:25:50 pm »
I don't know why you want to use objects at all. They lack virtual destructors and other advantages classes offer.
Here is one possible architecture that might fit.
 I have not implemented all the methods of TServer to save space, and there are lots of dummy classes just to avoid me installing the library units you will need. But I hope you get the general idea.
Code: Pascal  [Select]
  1. unit unitUtils;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Video;
  9.  
  10. type
  11.  
  12.   TLogMessage = record
  13.     Text: String;
  14.     ForeColor: Word;
  15.     BackColor: Word;
  16.     TimeStamp: TTimeStamp;
  17.     Prefix: String;
  18.   end;
  19.  
  20.   TLTcp = class
  21.   end;
  22.  
  23.   TLSocket = class
  24.   end;
  25.  
  26.   TOutputLog = String;
  27.  
  28.   TVideoCapabilities = record
  29.     cap1: Word;
  30.   end;
  31.  
  32.   //Data Storage Types
  33.   TInputCommand = record
  34.     Command: String;
  35.     ArgString: String;
  36.     HasArguments: Boolean;
  37.         end;
  38.  
  39.   TInputData = record
  40.     InputString: String;
  41.     InputBuffer: Array of String;
  42.     InputBufferIndex: Integer;
  43.     InputBufferSelected: Boolean;
  44.   end;
  45.  
  46.   TJSONClass = class
  47.     //
  48.   end;
  49.  
  50.   { TBaseHandler }
  51.  
  52.   TBaseHandler = class
  53.   private
  54.     FStringID: String;
  55.   public
  56.     constructor Create(aStringID: String);
  57.     property StringID: String read FStringID;
  58.     procedure HandleIt(aString: String); virtual; abstract;
  59.     procedure HandleIt(aJSON: TJSONClass); virtual; abstract;
  60.   end;
  61.   TBaseHandlerClass = class of TBaseHandler;
  62.  
  63.   { TConsoleHandler }
  64.  
  65.   TConsoleHandler = class(TBaseHandler)
  66.     procedure HandleIt(aString: String); override;
  67.   end;
  68.  
  69.   { TProtocolCommandHandler }
  70.  
  71.   TProtocolCommandHandler = class(TBaseHandler)
  72.     procedure HandleIt(aJSON: TJSONClass); override;
  73.   end;
  74.  
  75.   { TProtocolStatusHandler }
  76.  
  77.   TProtocolStatusHandler = class(TBaseHandler)
  78.     procedure HandleIt(aJSON: TJSONClass); override;
  79.   end;
  80.  
  81.   { TBaseModule }
  82.  
  83.   TBaseModule = class
  84.   private
  85.     FHandlerList: TFPList; // list of handlers
  86.   public
  87.     constructor Create;
  88.     destructor Destroy; override;
  89.     procedure Update; virtual; abstract;
  90.     procedure Init; virtual; abstract;
  91.     procedure Exit; virtual; abstract;
  92.     procedure AddHandler(aHandlerClass: TBaseHandlerClass; const aStringID: String);
  93.   end;
  94.  
  95.  { TServer = class(TBaseModule)
  96.   private
  97.     tcpServer: TLTcp;
  98.     ListenAddress: String;
  99.     ListenPort: Integer;
  100.     procedure tcpOnAccept(aSocket: TLSocket);
  101.     procedure tcpOnConnect(aSocket: TLSocket);
  102.     procedure tcpOnError(const msg: String; aSocket: TLSocket);
  103.     procedure tcpOnReceive(aSocket: TLSocket);
  104.     procedure tcpOnDisconnect(aSocket: TLSocket);
  105.     procedure tcpOnCanSend(aSocket: TLSocket);
  106.   public
  107.     constructor Create;
  108.     destructor Destroy; override;
  109.     procedure Exit; override;
  110.     procedure Init; override;
  111.     procedure Update; override;
  112.     function startListening(Address: String; Port: Integer): Boolean; //start the tcp server
  113.     function stopListening: Boolean;
  114.   end; }
  115.  
  116.   TConsole = class(TBaseModule)
  117.   private
  118.     InputData: TInputData;
  119.     OutputLog: TOutputLog;
  120.     procedure Draw;
  121.     function CheckCommandString(const cmdString: String): TInputCommand;
  122.     procedure getKeyboardInput;
  123.   public
  124.     Capabilities: TVideoCapabilities;
  125.     constructor Create;
  126.     destructor Destroy; override;
  127.     procedure Exit; override;
  128.     procedure Init; override;
  129.     procedure Update; override;
  130.     procedure UpdateDrawInfo;
  131.     procedure SendConsoleCommand(const cmdString: String); //send command to console
  132.     procedure WriteLogMessage(const Message: String; foreColor: Byte = White; backColor: Byte = Black; prefixString: String = 'MAIN/INFO');
  133.   end;
  134.  
  135. var
  136.   Console: TConsole = Nil;
  137.   //Server: TServer = Nil;
  138.  
  139. implementation
  140.  
  141. { TConsole }
  142.  
  143. procedure TConsole.Draw;
  144. begin
  145.  
  146. end;
  147.  
  148. function TConsole.CheckCommandString(const cmdString: String): TInputCommand;
  149. begin
  150.  
  151. end;
  152.  
  153. procedure TConsole.getKeyboardInput;
  154. begin
  155.  
  156. end;
  157.  
  158. constructor TConsole.Create;
  159. begin
  160.   inherited Create;
  161. end;
  162.  
  163. destructor TConsole.Destroy;
  164. begin
  165.   inherited Destroy;
  166. end;
  167.  
  168. procedure TConsole.Exit;
  169. begin
  170.  
  171. end;
  172.  
  173. procedure TConsole.Init;
  174. begin
  175.  
  176. end;
  177.  
  178. procedure TConsole.Update;
  179. begin
  180.  
  181. end;
  182.  
  183. procedure TConsole.UpdateDrawInfo;
  184. begin
  185.  
  186. end;
  187.  
  188. procedure TConsole.SendConsoleCommand(const cmdString: String);
  189. begin
  190.  
  191. end;
  192.  
  193. procedure TConsole.WriteLogMessage(const Message: String; foreColor: Byte;
  194.   backColor: Byte; prefixString: String);
  195. begin
  196.  
  197. end;
  198.  
  199. { TBaseModule }
  200.  
  201. constructor TBaseModule.Create;
  202. begin
  203.   FHandlerList := TFPList.Create;
  204. end;
  205.  
  206. destructor TBaseModule.Destroy;
  207. var
  208.   i: Integer;
  209. begin
  210.   for i := 0 to FHandlerList.Count-1 do
  211.     if TBaseHandler(FHandlerList[i]) is TConsoleHandler then
  212.       TConsoleHandler(FHandlerList[i]).Free
  213.     else if TBaseHandler(FHandlerList[i]) is TProtocolCommandHandler then
  214.       TProtocolCommandHandler(FHandlerList[i]).Free
  215.     else if TBaseHandler(FHandlerList[i]) is TProtocolStatusHandler then
  216.       TProtocolStatusHandler(FHandlerList[i]).Free
  217.     else Assert(True,'programmer error');
  218.   FHandlerList.Free;
  219.   inherited Destroy;
  220. end;
  221.  
  222. procedure TBaseModule.AddHandler(aHandlerClass: TBaseHandlerClass; const aStringID: String);
  223. begin
  224.   FHandlerList.Add(aHandlerClass.Create(aStringID));
  225. end;
  226.  
  227. { TProtocolStatusHandler }
  228.  
  229. procedure TProtocolStatusHandler.HandleIt(aJSON: TJSONClass);
  230. begin
  231.   with aJSON do
  232.     begin
  233.  
  234.     end;
  235. end;
  236.  
  237. { TProtocolCommandHandler }
  238.  
  239. procedure TProtocolCommandHandler.HandleIt(aJSON: TJSONClass);
  240. begin
  241.   with aJSON do
  242.     begin
  243.  
  244.     end;
  245. end;
  246.  
  247. { TConsoleHandler }
  248.  
  249. procedure TConsoleHandler.HandleIt(aString: String);
  250. begin
  251.   case aString of
  252.     'command1': begin   end;
  253.     'command2': begin   end;
  254.   end;
  255. end;
  256.  
  257. { TBaseHandler }
  258.  
  259. constructor TBaseHandler.Create(aStringID: String);
  260. begin
  261.   FStringID := aStringID;
  262. end;
  263.  
  264. initialization
  265.  
  266.   Console := TConsole.Create;
  267.   Console.AddHandler(TConsoleHandler, 'command1');
  268.   Console.AddHandler(TConsoleHandler, 'command2'); // etc
  269.  
  270.   {Server := TServer.Create;
  271.   Server.AddHandler(TProtocolCommandHandler, 'protocol1'); // etc }
  272.  
  273. finalization
  274.   Console.Free;
  275.  // Server.Free;
  276.  
  277. end.


JoeJoeTV

  • New Member
  • *
  • Posts: 40
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #5 on: October 09, 2019, 11:41:06 pm »
I don't know why you want to use objects at all. They lack virtual destructors and other advantages classes offer.
Here is one possible architecture that might fit.
 I have not implemented all the methods of TServer to save space, and there are lots of dummy classes just to avoid me installing the library units you will need. But I hope you get the general idea.
Code: Pascal  [Select]
  1. unit unitUtils;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Video;
  9.  
  10. type
  11.  
  12.   TLogMessage = record
  13.     Text: String;
  14.     ForeColor: Word;
  15.     BackColor: Word;
  16.     TimeStamp: TTimeStamp;
  17.     Prefix: String;
  18.   end;
  19.  
  20.   TLTcp = class
  21.   end;
  22.  
  23.   TLSocket = class
  24.   end;
  25.  
  26.   TOutputLog = String;
  27.  
  28.   TVideoCapabilities = record
  29.     cap1: Word;
  30.   end;
  31.  
  32.   //Data Storage Types
  33.   TInputCommand = record
  34.     Command: String;
  35.     ArgString: String;
  36.     HasArguments: Boolean;
  37.         end;
  38.  
  39.   TInputData = record
  40.     InputString: String;
  41.     InputBuffer: Array of String;
  42.     InputBufferIndex: Integer;
  43.     InputBufferSelected: Boolean;
  44.   end;
  45.  
  46.   TJSONClass = class
  47.     //
  48.   end;
  49.  
  50.   { TBaseHandler }
  51.  
  52.   TBaseHandler = class
  53.   private
  54.     FStringID: String;
  55.   public
  56.     constructor Create(aStringID: String);
  57.     property StringID: String read FStringID;
  58.     procedure HandleIt(aString: String); virtual; abstract;
  59.     procedure HandleIt(aJSON: TJSONClass); virtual; abstract;
  60.   end;
  61.   TBaseHandlerClass = class of TBaseHandler;
  62.  
  63.   { TConsoleHandler }
  64.  
  65.   TConsoleHandler = class(TBaseHandler)
  66.     procedure HandleIt(aString: String); override;
  67.   end;
  68.  
  69.   { TProtocolCommandHandler }
  70.  
  71.   TProtocolCommandHandler = class(TBaseHandler)
  72.     procedure HandleIt(aJSON: TJSONClass); override;
  73.   end;
  74.  
  75.   { TProtocolStatusHandler }
  76.  
  77.   TProtocolStatusHandler = class(TBaseHandler)
  78.     procedure HandleIt(aJSON: TJSONClass); override;
  79.   end;
  80.  
  81.   { TBaseModule }
  82.  
  83.   TBaseModule = class
  84.   private
  85.     FHandlerList: TFPList; // list of handlers
  86.   public
  87.     constructor Create;
  88.     destructor Destroy; override;
  89.     procedure Update; virtual; abstract;
  90.     procedure Init; virtual; abstract;
  91.     procedure Exit; virtual; abstract;
  92.     procedure AddHandler(aHandlerClass: TBaseHandlerClass; const aStringID: String);
  93.   end;
  94.  
  95.  { TServer = class(TBaseModule)
  96.   private
  97.     tcpServer: TLTcp;
  98.     ListenAddress: String;
  99.     ListenPort: Integer;
  100.     procedure tcpOnAccept(aSocket: TLSocket);
  101.     procedure tcpOnConnect(aSocket: TLSocket);
  102.     procedure tcpOnError(const msg: String; aSocket: TLSocket);
  103.     procedure tcpOnReceive(aSocket: TLSocket);
  104.     procedure tcpOnDisconnect(aSocket: TLSocket);
  105.     procedure tcpOnCanSend(aSocket: TLSocket);
  106.   public
  107.     constructor Create;
  108.     destructor Destroy; override;
  109.     procedure Exit; override;
  110.     procedure Init; override;
  111.     procedure Update; override;
  112.     function startListening(Address: String; Port: Integer): Boolean; //start the tcp server
  113.     function stopListening: Boolean;
  114.   end; }
  115.  
  116.   TConsole = class(TBaseModule)
  117.   private
  118.     InputData: TInputData;
  119.     OutputLog: TOutputLog;
  120.     procedure Draw;
  121.     function CheckCommandString(const cmdString: String): TInputCommand;
  122.     procedure getKeyboardInput;
  123.   public
  124.     Capabilities: TVideoCapabilities;
  125.     constructor Create;
  126.     destructor Destroy; override;
  127.     procedure Exit; override;
  128.     procedure Init; override;
  129.     procedure Update; override;
  130.     procedure UpdateDrawInfo;
  131.     procedure SendConsoleCommand(const cmdString: String); //send command to console
  132.     procedure WriteLogMessage(const Message: String; foreColor: Byte = White; backColor: Byte = Black; prefixString: String = 'MAIN/INFO');
  133.   end;
  134.  
  135. var
  136.   Console: TConsole = Nil;
  137.   //Server: TServer = Nil;
  138.  
  139. implementation
  140.  
  141. { TConsole }
  142.  
  143. procedure TConsole.Draw;
  144. begin
  145.  
  146. end;
  147.  
  148. function TConsole.CheckCommandString(const cmdString: String): TInputCommand;
  149. begin
  150.  
  151. end;
  152.  
  153. procedure TConsole.getKeyboardInput;
  154. begin
  155.  
  156. end;
  157.  
  158. constructor TConsole.Create;
  159. begin
  160.   inherited Create;
  161. end;
  162.  
  163. destructor TConsole.Destroy;
  164. begin
  165.   inherited Destroy;
  166. end;
  167.  
  168. procedure TConsole.Exit;
  169. begin
  170.  
  171. end;
  172.  
  173. procedure TConsole.Init;
  174. begin
  175.  
  176. end;
  177.  
  178. procedure TConsole.Update;
  179. begin
  180.  
  181. end;
  182.  
  183. procedure TConsole.UpdateDrawInfo;
  184. begin
  185.  
  186. end;
  187.  
  188. procedure TConsole.SendConsoleCommand(const cmdString: String);
  189. begin
  190.  
  191. end;
  192.  
  193. procedure TConsole.WriteLogMessage(const Message: String; foreColor: Byte;
  194.   backColor: Byte; prefixString: String);
  195. begin
  196.  
  197. end;
  198.  
  199. { TBaseModule }
  200.  
  201. constructor TBaseModule.Create;
  202. begin
  203.   FHandlerList := TFPList.Create;
  204. end;
  205.  
  206. destructor TBaseModule.Destroy;
  207. var
  208.   i: Integer;
  209. begin
  210.   for i := 0 to FHandlerList.Count-1 do
  211.     if TBaseHandler(FHandlerList[i]) is TConsoleHandler then
  212.       TConsoleHandler(FHandlerList[i]).Free
  213.     else if TBaseHandler(FHandlerList[i]) is TProtocolCommandHandler then
  214.       TProtocolCommandHandler(FHandlerList[i]).Free
  215.     else if TBaseHandler(FHandlerList[i]) is TProtocolStatusHandler then
  216.       TProtocolStatusHandler(FHandlerList[i]).Free
  217.     else Assert(True,'programmer error');
  218.   FHandlerList.Free;
  219.   inherited Destroy;
  220. end;
  221.  
  222. procedure TBaseModule.AddHandler(aHandlerClass: TBaseHandlerClass; const aStringID: String);
  223. begin
  224.   FHandlerList.Add(aHandlerClass.Create(aStringID));
  225. end;
  226.  
  227. { TProtocolStatusHandler }
  228.  
  229. procedure TProtocolStatusHandler.HandleIt(aJSON: TJSONClass);
  230. begin
  231.   with aJSON do
  232.     begin
  233.  
  234.     end;
  235. end;
  236.  
  237. { TProtocolCommandHandler }
  238.  
  239. procedure TProtocolCommandHandler.HandleIt(aJSON: TJSONClass);
  240. begin
  241.   with aJSON do
  242.     begin
  243.  
  244.     end;
  245. end;
  246.  
  247. { TConsoleHandler }
  248.  
  249. procedure TConsoleHandler.HandleIt(aString: String);
  250. begin
  251.   case aString of
  252.     'command1': begin   end;
  253.     'command2': begin   end;
  254.   end;
  255. end;
  256.  
  257. { TBaseHandler }
  258.  
  259. constructor TBaseHandler.Create(aStringID: String);
  260. begin
  261.   FStringID := aStringID;
  262. end;
  263.  
  264. initialization
  265.  
  266.   Console := TConsole.Create;
  267.   Console.AddHandler(TConsoleHandler, 'command1');
  268.   Console.AddHandler(TConsoleHandler, 'command2'); // etc
  269.  
  270.   {Server := TServer.Create;
  271.   Server.AddHandler(TProtocolCommandHandler, 'protocol1'); // etc }
  272.  
  273. finalization
  274.   Console.Free;
  275.  // Server.Free;
  276.  
  277. end.

Firt of all, thanks for replying, but I think you understoon a part of my question wrong:
I don't want TServer and TConsole to be the modules, I want to have the base module class defined in this unit and if I want to make a module I make a new unit and derive a class in that unit with the handlers and a registermodule function, which will call TConsole.RegisterCOmmandHandler, Tserver.RegisterStatusHandler, etc..
In my main unit, I will then call the function RegisterModule of the module, which registers it, so the handlers get added to their dedicated list, so I can call the handlers(from Tserver, Tconsole, etc.) when needed.

The Modules are supposed to be like a plug in, which provides handlers, update function, etc. that get called by the main unit.

Hope I explained that understandably.
Lazarus 2.0.4 / FPC 3.0.4 / 32+64bit / Windows 10

JoeJoeTV

  • New Member
  • *
  • Posts: 40
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #6 on: October 13, 2019, 04:11:00 pm »
I don't know why you want to use objects at all. They lack virtual destructors and other advantages classes offer.
Here is one possible architecture that might fit.
 I have not implemented all the methods of TServer to save space, and there are lots of dummy classes just to avoid me installing the library units you will need. But I hope you get the general idea.
Code: Pascal  [Select]
  1. unit unitUtils;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Video;
  9.  
  10. type
  11.  
  12.   TLogMessage = record
  13.     Text: String;
  14.     ForeColor: Word;
  15.     BackColor: Word;
  16.     TimeStamp: TTimeStamp;
  17.     Prefix: String;
  18.   end;
  19.  
  20.   TLTcp = class
  21.   end;
  22.  
  23.   TLSocket = class
  24.   end;
  25.  
  26.   TOutputLog = String;
  27.  
  28.   TVideoCapabilities = record
  29.     cap1: Word;
  30.   end;
  31.  
  32.   //Data Storage Types
  33.   TInputCommand = record
  34.     Command: String;
  35.     ArgString: String;
  36.     HasArguments: Boolean;
  37.         end;
  38.  
  39.   TInputData = record
  40.     InputString: String;
  41.     InputBuffer: Array of String;
  42.     InputBufferIndex: Integer;
  43.     InputBufferSelected: Boolean;
  44.   end;
  45.  
  46.   TJSONClass = class
  47.     //
  48.   end;
  49.  
  50.   { TBaseHandler }
  51.  
  52.   TBaseHandler = class
  53.   private
  54.     FStringID: String;
  55.   public
  56.     constructor Create(aStringID: String);
  57.     property StringID: String read FStringID;
  58.     procedure HandleIt(aString: String); virtual; abstract;
  59.     procedure HandleIt(aJSON: TJSONClass); virtual; abstract;
  60.   end;
  61.   TBaseHandlerClass = class of TBaseHandler;
  62.  
  63.   { TConsoleHandler }
  64.  
  65.   TConsoleHandler = class(TBaseHandler)
  66.     procedure HandleIt(aString: String); override;
  67.   end;
  68.  
  69.   { TProtocolCommandHandler }
  70.  
  71.   TProtocolCommandHandler = class(TBaseHandler)
  72.     procedure HandleIt(aJSON: TJSONClass); override;
  73.   end;
  74.  
  75.   { TProtocolStatusHandler }
  76.  
  77.   TProtocolStatusHandler = class(TBaseHandler)
  78.     procedure HandleIt(aJSON: TJSONClass); override;
  79.   end;
  80.  
  81.   { TBaseModule }
  82.  
  83.   TBaseModule = class
  84.   private
  85.     FHandlerList: TFPList; // list of handlers
  86.   public
  87.     constructor Create;
  88.     destructor Destroy; override;
  89.     procedure Update; virtual; abstract;
  90.     procedure Init; virtual; abstract;
  91.     procedure Exit; virtual; abstract;
  92.     procedure AddHandler(aHandlerClass: TBaseHandlerClass; const aStringID: String);
  93.   end;
  94.  
  95.  { TServer = class(TBaseModule)
  96.   private
  97.     tcpServer: TLTcp;
  98.     ListenAddress: String;
  99.     ListenPort: Integer;
  100.     procedure tcpOnAccept(aSocket: TLSocket);
  101.     procedure tcpOnConnect(aSocket: TLSocket);
  102.     procedure tcpOnError(const msg: String; aSocket: TLSocket);
  103.     procedure tcpOnReceive(aSocket: TLSocket);
  104.     procedure tcpOnDisconnect(aSocket: TLSocket);
  105.     procedure tcpOnCanSend(aSocket: TLSocket);
  106.   public
  107.     constructor Create;
  108.     destructor Destroy; override;
  109.     procedure Exit; override;
  110.     procedure Init; override;
  111.     procedure Update; override;
  112.     function startListening(Address: String; Port: Integer): Boolean; //start the tcp server
  113.     function stopListening: Boolean;
  114.   end; }
  115.  
  116.   TConsole = class(TBaseModule)
  117.   private
  118.     InputData: TInputData;
  119.     OutputLog: TOutputLog;
  120.     procedure Draw;
  121.     function CheckCommandString(const cmdString: String): TInputCommand;
  122.     procedure getKeyboardInput;
  123.   public
  124.     Capabilities: TVideoCapabilities;
  125.     constructor Create;
  126.     destructor Destroy; override;
  127.     procedure Exit; override;
  128.     procedure Init; override;
  129.     procedure Update; override;
  130.     procedure UpdateDrawInfo;
  131.     procedure SendConsoleCommand(const cmdString: String); //send command to console
  132.     procedure WriteLogMessage(const Message: String; foreColor: Byte = White; backColor: Byte = Black; prefixString: String = 'MAIN/INFO');
  133.   end;
  134.  
  135. var
  136.   Console: TConsole = Nil;
  137.   //Server: TServer = Nil;
  138.  
  139. implementation
  140.  
  141. { TConsole }
  142.  
  143. procedure TConsole.Draw;
  144. begin
  145.  
  146. end;
  147.  
  148. function TConsole.CheckCommandString(const cmdString: String): TInputCommand;
  149. begin
  150.  
  151. end;
  152.  
  153. procedure TConsole.getKeyboardInput;
  154. begin
  155.  
  156. end;
  157.  
  158. constructor TConsole.Create;
  159. begin
  160.   inherited Create;
  161. end;
  162.  
  163. destructor TConsole.Destroy;
  164. begin
  165.   inherited Destroy;
  166. end;
  167.  
  168. procedure TConsole.Exit;
  169. begin
  170.  
  171. end;
  172.  
  173. procedure TConsole.Init;
  174. begin
  175.  
  176. end;
  177.  
  178. procedure TConsole.Update;
  179. begin
  180.  
  181. end;
  182.  
  183. procedure TConsole.UpdateDrawInfo;
  184. begin
  185.  
  186. end;
  187.  
  188. procedure TConsole.SendConsoleCommand(const cmdString: String);
  189. begin
  190.  
  191. end;
  192.  
  193. procedure TConsole.WriteLogMessage(const Message: String; foreColor: Byte;
  194.   backColor: Byte; prefixString: String);
  195. begin
  196.  
  197. end;
  198.  
  199. { TBaseModule }
  200.  
  201. constructor TBaseModule.Create;
  202. begin
  203.   FHandlerList := TFPList.Create;
  204. end;
  205.  
  206. destructor TBaseModule.Destroy;
  207. var
  208.   i: Integer;
  209. begin
  210.   for i := 0 to FHandlerList.Count-1 do
  211.     if TBaseHandler(FHandlerList[i]) is TConsoleHandler then
  212.       TConsoleHandler(FHandlerList[i]).Free
  213.     else if TBaseHandler(FHandlerList[i]) is TProtocolCommandHandler then
  214.       TProtocolCommandHandler(FHandlerList[i]).Free
  215.     else if TBaseHandler(FHandlerList[i]) is TProtocolStatusHandler then
  216.       TProtocolStatusHandler(FHandlerList[i]).Free
  217.     else Assert(True,'programmer error');
  218.   FHandlerList.Free;
  219.   inherited Destroy;
  220. end;
  221.  
  222. procedure TBaseModule.AddHandler(aHandlerClass: TBaseHandlerClass; const aStringID: String);
  223. begin
  224.   FHandlerList.Add(aHandlerClass.Create(aStringID));
  225. end;
  226.  
  227. { TProtocolStatusHandler }
  228.  
  229. procedure TProtocolStatusHandler.HandleIt(aJSON: TJSONClass);
  230. begin
  231.   with aJSON do
  232.     begin
  233.  
  234.     end;
  235. end;
  236.  
  237. { TProtocolCommandHandler }
  238.  
  239. procedure TProtocolCommandHandler.HandleIt(aJSON: TJSONClass);
  240. begin
  241.   with aJSON do
  242.     begin
  243.  
  244.     end;
  245. end;
  246.  
  247. { TConsoleHandler }
  248.  
  249. procedure TConsoleHandler.HandleIt(aString: String);
  250. begin
  251.   case aString of
  252.     'command1': begin   end;
  253.     'command2': begin   end;
  254.   end;
  255. end;
  256.  
  257. { TBaseHandler }
  258.  
  259. constructor TBaseHandler.Create(aStringID: String);
  260. begin
  261.   FStringID := aStringID;
  262. end;
  263.  
  264. initialization
  265.  
  266.   Console := TConsole.Create;
  267.   Console.AddHandler(TConsoleHandler, 'command1');
  268.   Console.AddHandler(TConsoleHandler, 'command2'); // etc
  269.  
  270.   {Server := TServer.Create;
  271.   Server.AddHandler(TProtocolCommandHandler, 'protocol1'); // etc }
  272.  
  273. finalization
  274.   Console.Free;
  275.  // Server.Free;
  276.  
  277. end.

Hey, i looked through it again and I have a question:
In the procedure TBaseModule.AddHandler you have aHandlerClass of type TBaseHandlerClass as a parameter, but how can you then use the other handler types here?
And when the pointer gets added to the list, it's also just the aHandlerClass class, does the create return a pointer to the class instance?
I'm still new to classes, so please don't judge me too hard.
Lazarus 2.0.4 / FPC 3.0.4 / 32+64bit / Windows 10

howardpc

  • Hero Member
  • *****
  • Posts: 3148
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #7 on: October 13, 2019, 05:11:34 pm »
Hey, i looked through it again and I have a question:
In the procedure TBaseModule.AddHandler you have aHandlerClass of type TBaseHandlerClass as a parameter, but how can you then use the other handler types here?
The other handler types are all descendants of TBaseHandler, so they are compatible classes by definition. This means you have to do rather tedious interrogation to discover the actual handler class at runtime, to use methods specific to a particular class. See the Destroy method for an example. But that bit of tedious boilerplate code means you have then have the power of polymorphism at your disposal: you can call the appropriate (virtual) method of the handler.

Quote
And when the pointer gets added to the list, it's also just the aHandlerClass class, does the create return a pointer to the class instance?
Yes, and that reference is stored for future use in the list.

JoeJoeTV

  • New Member
  • *
  • Posts: 40
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #8 on: October 13, 2019, 06:14:21 pm »
Thank you for explaining this, but one question I still have is, how does virtual and abstract work, can you declare multiple versions of a procedure or function with virtual?
Lazarus 2.0.4 / FPC 3.0.4 / 32+64bit / Windows 10

howardpc

  • Hero Member
  • *****
  • Posts: 3148
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #9 on: October 13, 2019, 06:58:38 pm »
Yes, that is the point of virtual methods.
You reimplement them for each class that needs a specific adaptation, and the correct method is called for that class instance, even though all such methods in the class hierarchy have the same name.

Thaddy

  • Hero Member
  • *****
  • Posts: 8898
Re: Need help with Objects/Classes and lists of Objects/Classes
« Reply #10 on: October 13, 2019, 08:08:54 pm »
Additional to Howard's answer:
Usually, with classes, you declare them virtual when they are first declared and override them when you want to add or change behavior in descendant classes.
With object (old school), not classes, you always use virtual and not override.
Most people that want to use threading should learn to patch their jeans first: use a needle.