Marc : Conversion process and questions about it
- Marc Venken
- Posts: 1485
- Joined: Tue Jun 14, 2016 7:51 am
- Location: Belgium
Re: Marc : Conversion process and questions about it
James,
One of your favorites is also a question....
I used in early days rather small object programming, and at this point i'm not going to changes a lot of it, more if new stuff wil be added it will become a option.
I used
DATABASE oKlant // my early way
and then stuff for oKlant
I see many times :
oCustomers:= TDatabase():New(,"temp",,.f.)
oCustomers:use()
Is the second way the way it should be done in order to use ALL options in FWH or is it the same ?
If I reachs the process of TDATABASE i will come back on this options...
One of your favorites is also a question....
I used in early days rather small object programming, and at this point i'm not going to changes a lot of it, more if new stuff wil be added it will become a option.
I used
DATABASE oKlant // my early way
and then stuff for oKlant
I see many times :
oCustomers:= TDatabase():New(,"temp",,.f.)
oCustomers:use()
Is the second way the way it should be done in order to use ALL options in FWH or is it the same ?
If I reachs the process of TDATABASE i will come back on this options...
Marc Venken
Using: FWH 23.08 with Harbour
Using: FWH 23.08 with Harbour
- James Bott
- Posts: 4840
- Joined: Fri Nov 18, 2005 4:52 pm
- Location: San Diego, California, USA
- Contact:
Re: Marc : Conversion process and questions about it
Marc,
You really need to avoid filters. Filters require reading the entire database each time you call them. So for an incremental search of 10 characters you are going to read the entire database 10 times.
Even if you don't do an incremental search, you still have to read the entire database using a filter. Using a scope you only read the database records you need. Way faster!
Another possibility is to use a scope, then filter only the records in the scope.
You really need to avoid filters. Filters require reading the entire database each time you call them. So for an incremental search of 10 characters you are going to read the entire database 10 times.
Even if you don't do an incremental search, you still have to read the entire database using a filter. Using a scope you only read the database records you need. Way faster!
Another possibility is to use a scope, then filter only the records in the scope.
FWH 18.05/xHarbour 1.2.3/BCC7/Windows 10
- Marc Venken
- Posts: 1485
- Joined: Tue Jun 14, 2016 7:51 am
- Location: Belgium
Re: Marc : Conversion process and questions about it
James Bott wrote:Marc,
You really need to avoid filters. Filters require reading the entire database each time you call them. So for an incremental search of 10 characters you are going to read the entire database 10 times.
Even if you don't do an incremental search, you still have to read the entire database using a filter. Using a scope you only read the database records you need. Way faster!
Another possibility is to use a scope, then filter only the records in the scope.
I use scoped filter... They are fast.
BTW : Speed is not the problem. All by all... it is fast enough, but optimising code can speed things up, unless the functions I posted are not correct ??
Marc Venken
Using: FWH 23.08 with Harbour
Using: FWH 23.08 with Harbour
- James Bott
- Posts: 4840
- Joined: Fri Nov 18, 2005 4:52 pm
- Location: San Diego, California, USA
- Contact:
Re: Marc : Conversion process and questions about it
DATABASE oKlant // my early way
and then stuff for oKlant
I see many times :
oCustomers:= TDatabase():New(,"temp",,.f.)
oCustomers:use()
Is the second way the way it should be done in order to use ALL options in FWH or is it the same ?
You can use all options of the database when it is an object. One of the great things when you use an object is that you don't have to deal with workareas at all. You can even have multiple copies of the same database open at the same time, even in the function. This allows you to open a database in a function, use it, then close it, even while it is open in another function. Without this capability you had to save and restore the database state (including workarea, indexes, filters, scopes, recno, etc.).
Also you should not open a database object like in your second example. You should always try to write code only once. This reduces code and thus the chance of errors.
Also you want to emulate the real world, so you create a customers class and open the database and index(s) in the class.
Code: Select all | Expand
Class TCustomers from TDatabase
Method New()
Endclass
Method New Class TCustomers
::use(,"Customers","customers")
Return self
Note that I use the class name "Customers" (plural). This is because the database is full of single customer records and we also need a "TCustomer" class for a single customer object. So now we can do:
Code: Select all | Expand
oCustomers := TCustomers():new() // create the table object
oCustomers:seek("12345")
Now the code to open a database exists only in the object's new method. Thus if you want to change or add an index, you only have one place to modify. Also if you want to change the location of the database, you do it in the new method only. Actually better would be to create a parent object for all databases, that specifies the location, then all database class inherit from that parent class. Now the location only exists in one place.
Now we can create a single customer object.
Code: Select all | Expand
oCustomer:= TCustomer():New(oCustomers:custno) // create a customer object
So now we can use the customer object to query for name, address, balance, past due, last invoice, etc. You can also pass it to another object or function and within that object or function you can get access all the object's data and methods. Note that you add methods to the object instead of using functions. This follows the rule of "encapsulation." You encapsulate "functions" for customers into methods of that object.
Code: Select all | Expand
oCustomer:AcceptPayment( nAmount, dDate)
Since the customer object is a smart object it knows how to update any appropriate databases with the payment information.
Using this technology I have taken existing programs and reduced the code by 50-70 percent. This is because you can eliminate a lot of repetitive code, and less code means less chance of errors, and it is much easier to read and understand.
FWH 18.05/xHarbour 1.2.3/BCC7/Windows 10
- Marc Venken
- Posts: 1485
- Joined: Tue Jun 14, 2016 7:51 am
- Location: Belgium
Re: Marc : Conversion process and questions about it
For my customer update, i'm going for objects ....
I found this template sample from Mr. Rao
http://forums.fivetechsupport.com/viewtopic.php?f=3&t=37080#p221386
This has several of the basic commands to start with OOP.
Which are the most used methods that I have to look into ? File is classes/database
I found this template sample from Mr. Rao
http://forums.fivetechsupport.com/viewtopic.php?f=3&t=37080#p221386
This has several of the basic commands to start with OOP.
Which are the most used methods that I have to look into ? File is classes/database
Marc Venken
Using: FWH 23.08 with Harbour
Using: FWH 23.08 with Harbour
- James Bott
- Posts: 4840
- Joined: Fri Nov 18, 2005 4:52 pm
- Location: San Diego, California, USA
- Contact:
Re: Marc : Conversion process and questions about it
Marc,
Well this seems to be an example of how to edit the properties of a database. I would not get sidetracked with that right now in your learning curve.
Most object programming texts advocate separating the interface code from real-world objects. Thus a customer object should emulate a real-world customer and not include data entry screens, browses, etc. There is nothing wrong with creating classes for those types of things too, but I suggest you trying emulating real-world objects first. They are simpler and easier to grasp, IMHO.
Have you read my introductory articles about OOP https://gointellitech.com/program.html? If not, I think they will help. Note that they are very dated so some of the things I wrote then I would not advocate today.
Well this seems to be an example of how to edit the properties of a database. I would not get sidetracked with that right now in your learning curve.
Most object programming texts advocate separating the interface code from real-world objects. Thus a customer object should emulate a real-world customer and not include data entry screens, browses, etc. There is nothing wrong with creating classes for those types of things too, but I suggest you trying emulating real-world objects first. They are simpler and easier to grasp, IMHO.
Have you read my introductory articles about OOP https://gointellitech.com/program.html? If not, I think they will help. Note that they are very dated so some of the things I wrote then I would not advocate today.
FWH 18.05/xHarbour 1.2.3/BCC7/Windows 10
- Marc Venken
- Posts: 1485
- Joined: Tue Jun 14, 2016 7:51 am
- Location: Belgium
Re: Marc : Conversion process and questions about it
I wonder if we can use this technique also for the mem variables ?
During programming we define a lot of local variables
local cNum:="", nTel:=0 .....
Maybe you also make a object out of it, so that ALL vars or at least those that are used othen on several places can be set into the object so that they can be easily passed..
I'm thinking more of system variables that are used public at my software.
Sys_date, Sys_Path, Sys_Periode (public variables set at startup)
can become :
oSystem:Date
oSystem:Path
The ease would be that they are all located into the object.
During programming we define a lot of local variables
local cNum:="", nTel:=0 .....
Maybe you also make a object out of it, so that ALL vars or at least those that are used othen on several places can be set into the object so that they can be easily passed..
I'm thinking more of system variables that are used public at my software.
Sys_date, Sys_Path, Sys_Periode (public variables set at startup)
can become :
oSystem:Date
oSystem:Path
The ease would be that they are all located into the object.
Marc Venken
Using: FWH 23.08 with Harbour
Using: FWH 23.08 with Harbour
- Marc Venken
- Posts: 1485
- Joined: Tue Jun 14, 2016 7:51 am
- Location: Belgium
Re: Marc : Conversion process and questions about it
I see that there is a advice to use a prg for each object.
So the intension would be a
prg file for Customers
prg file for Xbrowsers
....
I see the goal of that of having all customers stuff in a customer file
This is still correct anno 2020 FWH ?
So the intension would be a
prg file for Customers
prg file for Xbrowsers
....
I see the goal of that of having all customers stuff in a customer file
This is still correct anno 2020 FWH ?
Marc Venken
Using: FWH 23.08 with Harbour
Using: FWH 23.08 with Harbour
- Antonio Linares
- Site Admin
- Posts: 42604
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Has thanked: 44 times
- Been thanked: 88 times
- Contact:
Re: Marc : Conversion process and questions about it
Marc,
To use oSystem:Date and oSystem:Path, you need to use an object of a class like this:
#include "hbclass.ch"
CLASS System
DATA date
DATA path
ENDCLASS
oSystem = System()
oSystem:date = Date()
oSystem:path = hb_CurDrive() + ":\" + CurDir()
another alternative is to use a "hash" where you don't need a class:
local hSystem := {=>}
hSystem[ "date" ] = Date()
hSystem[ "path" ] = hb_CurDrive() + ":\" + CurDir()
hSystem[ "another" ] = 123
as you see you can have more info inside it without having to keep a Class updated
Yes, use different PRGs to keep properly organized the different parts of your app.
"Divide and conquer", the key of structured programming
To use oSystem:Date and oSystem:Path, you need to use an object of a class like this:
#include "hbclass.ch"
CLASS System
DATA date
DATA path
ENDCLASS
oSystem = System()
oSystem:date = Date()
oSystem:path = hb_CurDrive() + ":\" + CurDir()
another alternative is to use a "hash" where you don't need a class:
local hSystem := {=>}
hSystem[ "date" ] = Date()
hSystem[ "path" ] = hb_CurDrive() + ":\" + CurDir()
hSystem[ "another" ] = 123
as you see you can have more info inside it without having to keep a Class updated
Yes, use different PRGs to keep properly organized the different parts of your app.
"Divide and conquer", the key of structured programming

Re: Marc : Conversion process and questions about it
Another option is to use a CLASS already developed by Andrade A. Daniel. The CLASS's name is TPublic
Instead of passing many variables to other functions as parameters, you can simply pass one single object
For eg
Instead of using YourFuncName(nBillNo,dBillDate,cCustCode,cCustName,......)
You can easily pass this one single object ie
YourFuncName(oBill)
This will help you to maintain your code neatly. On a later date if you need to pass one more additional variable to the function, then you don't have to modify the function "YourFuncName(oBill)" to receive more parameters.
The Hash is also a good alternative as shown by Mr.Antonio
Code: Select all | Expand
// Usage
oBill:=TPublic():New(.T.)
// Vaiables Declaration
oBill:nBillNo:=100
oBill:dBillDate:=Date()
oBill:cCustCode:=Space(10)
oBill:cCustName:=Space(35)
Instead of passing many variables to other functions as parameters, you can simply pass one single object
For eg
Instead of using YourFuncName(nBillNo,dBillDate,cCustCode,cCustName,......)
You can easily pass this one single object ie
YourFuncName(oBill)
This will help you to maintain your code neatly. On a later date if you need to pass one more additional variable to the function, then you don't have to modify the function "YourFuncName(oBill)" to receive more parameters.
The Hash is also a good alternative as shown by Mr.Antonio
- Marc Venken
- Posts: 1485
- Joined: Tue Jun 14, 2016 7:51 am
- Location: Belgium
Re: Marc : Conversion process and questions about it
Exactly what I was thinking about. Thanks eveyone.
I found the class in the forum : http://forums.fivetechsupport.com/viewtopic.php?f=6&t=38871&hilit=tpublic
Antonio asked about it. Maybe it is inside FWH already ?? I have to test/see in the source files
Otherwise, If I want to use it, I copy it to tpublic.prg and just link it in? And this is so for every class I would make.
I found the class in the forum : http://forums.fivetechsupport.com/viewtopic.php?f=6&t=38871&hilit=tpublic
Antonio asked about it. Maybe it is inside FWH already ?? I have to test/see in the source files
Otherwise, If I want to use it, I copy it to tpublic.prg and just link it in? And this is so for every class I would make.
Marc Venken
Using: FWH 23.08 with Harbour
Using: FWH 23.08 with Harbour
- James Bott
- Posts: 4840
- Joined: Fri Nov 18, 2005 4:52 pm
- Location: San Diego, California, USA
- Contact:
Re: Marc : Conversion process and questions about it
Marc,
From a previous post near the top of this message topic you said:
Not exactly. Remember you need to write each piece of code only once. Thus, define a class for each database like the sample below. I put all the database classes into one PRG. Now your database filenames are all in one place, and thye only exist in that one place. So if you need to change them, it requires only one simple name change.
So now everywhere you need to use the customers database, you just do:
oCustomers:= TCustomers:New()
Simple!
That alone will probably eliminate hundreds of lines of code in your program.
The variable oCustomers should be declared as LOCAL. Note that you can still pass oCustomers to a function if you wish.
One last thing, I am using CDXs that auto-open when the database is opened thus you don't need to deal with them. I also always have the primary key index as the first index so you don't need to set it as the current index when opening the file. More lines of code are eliminated this way.
From a previous post near the top of this message topic you said:
I used
DATABASE oKlant // my early way
and then stuff for oKlant
I see many times :
oCustomers:= TDatabase():New(,"temp",,.f.)
oCustomers:use()
Is the second way the way it should be done in order to use ALL options in FWH or is it the same ?
Not exactly. Remember you need to write each piece of code only once. Thus, define a class for each database like the sample below. I put all the database classes into one PRG. Now your database filenames are all in one place, and thye only exist in that one place. So if you need to change them, it requires only one simple name change.
Code: Select all | Expand
Class TCustomers from TDatabase
Method New()
Endclass
Method New( lShared ) Class TCustomers
Default lShared:= .T.
super:new(,"Customers",,lShared) // database filename only exists here.
::use()
Return Self
So now everywhere you need to use the customers database, you just do:
oCustomers:= TCustomers:New()
Simple!
That alone will probably eliminate hundreds of lines of code in your program.
The variable oCustomers should be declared as LOCAL. Note that you can still pass oCustomers to a function if you wish.
One last thing, I am using CDXs that auto-open when the database is opened thus you don't need to deal with them. I also always have the primary key index as the first index so you don't need to set it as the current index when opening the file. More lines of code are eliminated this way.
FWH 18.05/xHarbour 1.2.3/BCC7/Windows 10
- Marc Venken
- Posts: 1485
- Joined: Tue Jun 14, 2016 7:51 am
- Location: Belgium
Re: Marc : Conversion process and questions about it
The variable oCustomers should be declared as LOCAL. Note that you can still pass oCustomers to a function if you wish.
Why not public ? Then the oCustomers is visible everywhere and we don't have to paste them by a function.
Marc Venken
Using: FWH 23.08 with Harbour
Using: FWH 23.08 with Harbour
- Antonio Linares
- Site Admin
- Posts: 42604
- Joined: Thu Oct 06, 2005 5:47 pm
- Location: Spain
- Has thanked: 44 times
- Been thanked: 88 times
- Contact:
Re: Marc : Conversion process and questions about it
Think about the app as an entire object. The app has its own datas and methods (behaviors) of its class.
CLASS TApp
DATA aPublics INIT {}
METHOD New( ... )
....
ENDCLASS
The entire app is just an object
CLASS TApp
DATA aPublics INIT {}
METHOD New( ... )
....
ENDCLASS
The entire app is just an object
- James Bott
- Posts: 4840
- Joined: Fri Nov 18, 2005 4:52 pm
- Location: San Diego, California, USA
- Contact:
Re: Marc : Conversion process and questions about it
Marc,
I just saw this old message:
One reason is that using a public violates the object rule of encapsulation. You can open a new copy of the customers object anywhere you want, then close it when you are done. This way you don't have to save and restore the state of a passed database object. You could have many copies of the same database open at any time and none would conflict with any of the others. For instance you could have a oCustomer object (single customer not a table) as a variable of the invoice class. Note also that the database objects handle finding and using a unique workarea so you never have to deal with workareas again.
oInvoice:= TInvoice():new(cInvoice:ID)
MsgInfo( oInvoice:oCustomer:name )
To do this the invoice object opens a copy of the oCustomers object in the oInvoice:New() method and it looks up the customer record and creates a oCustomer (singular) object inside the TInvoice class.
I never use publics.
I just saw this old message:
The variable oCustomers should be declared as LOCAL. Note that you can still pass oCustomers to a function if you wish.
Why not public? Then the oCustomers is visible everywhere and we don't have to paste them by a function.
One reason is that using a public violates the object rule of encapsulation. You can open a new copy of the customers object anywhere you want, then close it when you are done. This way you don't have to save and restore the state of a passed database object. You could have many copies of the same database open at any time and none would conflict with any of the others. For instance you could have a oCustomer object (single customer not a table) as a variable of the invoice class. Note also that the database objects handle finding and using a unique workarea so you never have to deal with workareas again.
oInvoice:= TInvoice():new(cInvoice:ID)
MsgInfo( oInvoice:oCustomer:name )
To do this the invoice object opens a copy of the oCustomers object in the oInvoice:New() method and it looks up the customer record and creates a oCustomer (singular) object inside the TInvoice class.
I never use publics.
FWH 18.05/xHarbour 1.2.3/BCC7/Windows 10