[Harbour-users] ORMs (from "thread" GUI programming)
Teo Fonrouge
teo at windtelsoft.com
Tue Mar 16 20:31:08 EDT 2010
Hello xProgrammer,
On Mar 16, 2010, at 4:56 PM, dougf at people.net.au wrote:
> Hi all
>
> Its nice to see that a discussion that started out with some highly
> questionable
> assertions about OOP has turned right around to a discussion of ORMs
> which
> essentially extend the OOP concept.
>
> Harbour doesn't force OOP on anyone, but I suspect most savvy xBase
> coders head
> that way. Even those that write procedural code are using some objects,
> hidden from them by the preprocessor. Beginners soon realise that they
> are
> writing code that pretty much does something similar to what they have
> done
> before. At first they may use the "cut and paste" approach. Most then
> come to
> realise that this approach is not ideal because
>
> 1. Its easy to miss bits of code that need to be altered
>
> 2. To change "standard behaviour" you have to change code in many places
>
> So most of us ended up with a whole series of "utility functions" which
> identified (as parameters) those things that changed from invocation to
> invocation and meant that standard behaviour could be changed by altering
> a
> single piece of code.
>
> OOP basically extends this in a more powerful and consistent way.
>
> But if you don't like writing "standard" Harbour OOP code that might look
> like
>
> obj_MyWindow := TWindow():New( 500, 300, "Sample Window" )
>
> you can use the preprocessor and write something like
>
> CREATE WINDOW myWindow TITLE "Sample Window" SIZE 500, 300
>
> all it takes is a preprocessor directive like
>
> #xcommand CREATE WINDOW TITLE SIZE , => ;
> := TWindow():New( , , )
>
> That is one of the fundamental beauties of xBase.
>
> I would also note that the popularity of VB is due to Microsoft's
> marketing power and the fact
> that many of us are effectively forced to use it (or its sibling VBA) to
> automate office type tasks.
> If you judged languages by their popularity you wouldn't be using Harbour
> despite the fact
> that its so wonderful a language.
>
> Now onto the interesting part - ORMs.
>
> These should fit very neatly into Harbour. Take an "example" from a
> previous post:
>
> emp=Employee.new
> emp.name = "Emp1"
> emp.basic = 4500
> emp.designation = "Programmer"
> emp.save
>
> Harbour equivalents are easy:
>
> obj_Employee := TEmployee():New()
> WITH OBJECT obj_Employee
> :Name := "Emp1"
> :Basic := 4500
> :Designation := "Programmer"
> :Save()
> END
>
> Of course there are many alternatives (depending in part on the TEmployee
> class) such as
>
> obj_Employee := TEmployeeNew( "Emp1", 4500, "Programmer" )
> obj_Employee:Save()
>
> Or with suitable preprocessor directive for those that like the object
> orientation hidden
>
> CREATE EMPLOYEE ThisEmployee
> ThisEmployee:Name := "Emp1"
> ThisEmployee:Basic := 4500
> ThisEmployee:Designation := Programmer
> SAVE EMPLOYEE ThisEmployee
>
> Obviously we have to define the TEmployee Class which would inherit from
> a class that
> "understands" how to read / write etc but not the particular details of
> an employee record,
> something like:
>
> CLASS TEmployee FROM TSingleRecord
>
> DATA Name
> DATA Basic
> DATA Designation
> // etc
>
> ENDCLASS
>
> Methods like Save() would be inherited from TSingleRecord as might its
> oid (object
> identifier / primary key )
Exactly.
>
> CLASS TSingleRecord
>
> DATA oid
> // etc
>
> METHOD Save()
> // etc
>
> ENDCLASS
>
> This is very much along the lines of the approach I use combined with a
> client-server data
> base back end I wrote for my own use. A record is returned as a
> two-dimensional array that
> can be used to directly update the front end object. The array is
> serialsed and transmitted
> using IP sockets. Properties are directly updated using
> __ObjSetValueList(). This type of
> approach would seem ideal for an ORM in my opinion.
I'll add that to fulfill this approach, we need to have at least:
* Abstraction
* Inheritance
* Polymorphism
e.g., The following sample creates three new databases:
TEmployer
TPerson
TEmployee
fills the databases with some values and then prints it out:
<prg>
/* Contains the Employer's database */
CLASS TEmployer FROM TTable
DATA tableName INIT "employer" // the physical file data base
DEFINE FIELDS // Load the Field's information
ENDCLASS
BEGIN FIELDS CLASS TEmployer
/* the primary key database */
ADD PRIMARYKEY FIELD "Employer" AUTOINCREMENTAL
ADD STRING FIELD "FirstName" SIZE 100
ADD STRING FIELD "LastName" SIZE 100
ADD STRING "Phone" SIZE 40
/* creates a TEmployees table whos MasterSource
is this database (TEmployer) */
ADD OBJECT FIELD "Employees" CLASS "TEmployee"
END FIELDS
/* Contains the Person's database */
CLASS TPerson FROM TTable
DATA tableName INIT "persemp" // the physical file data base
DEFINE FIELDS // Load the Field's information
/* calculated fields */
CALCFIELD Age INLINE Years( ::Field_Birth )
CALCFIELD FullName INLINE RTrim( ::Field_LastName ) + ", " + RTrim( ::Field_FirstName )
ENDCLASS
BEGIN FIELDS CLASS TPerson
/* the primary key database */
ADD PRIMARYKEY FIELD "Person" AUTOINCREMENTAL
ADD STRING FIELD "FirstName" SIZE 40
ADD DATE FIELD "Birth" ;
VALID {|d| Year( d ) > 1850 } /* validation for field */
ADD STRING FIELD "Genre" ;
VALID {|g| g $ "FM" } /* validation for field */
END FIELDS
/* Contains the Employee's database */
CLASS TEmployee FROM TPerson
DEFINE FIELDS // Load the Field's information
CALCFIELD Age INLINE Years( ::Field_HiredOn )
ENDCLASS
BEGIN FIELDS CLASS TEmployee
/* the master key database */
ADD OBJECT FIELD "Employer" CLASS "TEmployer" MASTERKEY
ADD FLOAT FIELD "Salary" ;
VALID {|s| s >= 0 } /* validation for field */
ADD DATE FIELD "HiredOn" ;
VALID {|d| Year( d ) > 1850 } /* validation for field */
/* the primary key database */
ADD PRIMARYKEY FIELD "Person"
END FIELDS
FUNCTION Main()
emp := TEmployer():New()
/* adds a new TEmployer record */
emp:NewRecord()
emp:Field_Name := "US ROBOTICS"
emp:Field_Phone := "1 555 555555"
/* adds a new TEmployee record */
emp:Field_Employees:NewRecord()
emp:Field_Employees:Field_FirstName := "John"
emp:Field_Employees:Field_LastName := "A"
emp:Field_Employees:Field_Birth := "1/1/1970"
emp:Field_Employees:Field_HiredOn := "1/1/2000"
emp:Field_Employees:Field_Genre := "M"
emp:Field_Employees:Field_Salary := 80000
emp:Field_Employees:Post()
pers := TPerson():New()
/* adds a new TPerson record */
pers:NewRecord()
pers:Field_FirstName := "Jane"
pers:Field_LastName := "B"
pers:Field_Birth := "1/1/1980"
pers:Field_Genre := "F"
pers:Post()
/* adds the previous TPerson record to TEmployer */
emp:Field_Employees:NewRecordFrom( pers )
emp:Field_Employees:Field_Salary := 80000
emp:Field_Employees:Field_HiredOn := "1/1/2000"
emp:Field_Employees:Post()
/* prints the whole TEmployer database with his employees */
WHILE !emp:Eof()
? "Employer: ", emp:Field_Name, emp:Field_Phone
WHILE !emp:Field_Employees:Eof()
? " Employee:"
? " Genre:", emp:Field_Employees:Field_Genre
? " FullName:", emp:Field_Employees:Field_FullName
? " Age:", emp:Field_Employees:Field_Age
? " Salary:", emp:Field_Employees:Field_Salary
emp:Field_Employees:Skip()
ENDDO
emp:Skip()
ENDDO
RETURN NIL
</prg>
best regards,
Teo
More information about the Harbour-users
mailing list