My understanding of OOP revolves around three principles: ENCAPSULATION: All data-Types, Procedures, Functions are placed within a new Type of wrapper called an Object. This new wrapper is very simillar to a standard Record structure, except that it also contains the routines that will act on the data-Types within the Object. The Object-oriented style of Programming requires that you should ONLY use the routines within the Object to modify/retrieve each Object's data-Types. (ie: Don't access the Variables directly.) Structured Style OOP Style ================ ========= MyRecord = Record MyObject = Object 1st Variable; 1st Variable; 2nd Variable; 2nd Variable; 3rd Variable 3rd Variable; end; Procedure One; Procedure Two; Function One; Function Two; end; inHERITANCE: This gives you the ability to make a new Object by cloning an old Object. The new Object will contain all the abilities of the old Object. (ie: Variables, Procedures/Functions). You can add additional abilities to this new Object, or replace old ones. +--------------+ | New Object | | +--------+ | | | Old | | | | Object | | | +--------+ | +--------------+ With Inheritance, you don't have to go back and re-Write old routines to modify them into new ones. Instead, simply clone the old Object and add or replace Variables/Procedures/Functions. This makes the whole process of rewriting/modifying a Program MUCH faster/easier. Also there is less chance of creating new bugs from your old bug-free source-code. POLYMorPHISM: The name Sounds intimidating, but the concept is simple. Polymorphism allows one Procedure/Function to act differently between one Object and all its descendants. (Clones) These Type of "polymorphic" Procedures/Functions know which Object they are working on, and act accordingly. For example: Say you've created an Object (Object-1) that contains a Procedure called DrawWindow, to draw the main screen of a Program. DrawWindow relies on another Procedure SetBorder within Object-1, to set the borders used in the main screen. Now you clone Object-2 from Object-1. You want to use Object-2 to handle pop-up Windows, but you want the pop-ups to have a different border style. if you call the DrawWindow Procedure that Object-2 inherited from Object-1, you'll end up With a Window With the wrong border-style. to get around this you could change the SetBorder Procedure to a "Virtual" Procedure, and add a second identically named "Virtual" Procedure (SetBorder) within Object-2. A "Virtual" Procedure relies on a "Virtual Table" (Which is basicly a Chart to indicate which "Virtual" routine belongs to which Object) to, indicate which version of the identically named Procedures should be used within different Objects. So within Object-1, the DrawWindow routine will use the SetBorder Procedure within Object-1. Within Object-2, the inherited DrawWindow routine will use the other SetBorder Procedure that belongs to Object-2. This works because the "Virtual Table" tells the DrawWindow routine which SetBorder Procedure to use For each different Object. So a call to the SetBorder Procedure now acts differently, depending on which Object called it. This is "polymorphism" in action. OOP LANGUAGE LinGO: The following are some of the proper names For OOP syntax. Structured Programming OOP Programming ====================== =============== Variables Instances Procedures/Functions Methods Types Classes Records Objects { > i have a parent Object defined With Procedure a and b. > i have a child Object With Procedure a, b and c. > when i declare say john being a child, i can use a, b, or c With no > problem. when i declare john as being a parent, i can use a or b. > if i declare john as being a parent and initialise it with > new (childPTR,init) it seems i have access to the parent fields After reading twice, I understand you mean Object classes dealing With humans, not trees (happen to have parents & childs too). > parent a,b,c,d,e,f (bad) > parent a,b (good) > child a,b,c > child2 a,b,d > child3 a,b,e,f (redefine a, b For childs as Far as they differ from parent a,b) Next example could be offensive For christians, atheists and media-people. } Type TParent = Object { opt. (tObject) For Stream storage } Name : String; Constructor Init(AName: String); Procedure Pray; { your A, they all do it the same way } Procedure Decease; Virtual; { your B, Virtual, some instances behave different (Heaven/Hell) } Destructor Done; Virtual; end; TChild1 = Object(TParent) Disciples : Byte; Constructor Init(AName: String; DiscipleCount: Byte); { do not override Decease } { calling it will result in a call to TParent.Decease } Procedure Resurrection; { your C } end; TChild2 = Object(TParent) BulletstoGo : LongInt; Constructor Init(DisciplesCount: Byte; Ammo: LongInt); Procedure Decease; Virtual; { override } Procedure Phone(Who: Caller); { your D } end; Constructor TParent.Init(AName: String); begin Name := AName; end; Destructor TParent.Done; begin {(...)} end; Procedure TParent.Pray; begin ContactGod; end; Procedure TParent.Decease; begin GotoHeaven; end; Constructor TChild1.Init(AName: String; DiscipleCount: Byte); begin inherited Init(AName); Disciples := DiscipleCount; end; Procedure TChild1.Resurrection; begin RiseFromTheDead; end; Constructor TChild2.Init(AName: String; DiscipleCount: Byte; Ammo: LongInt); begin inherited Init(DiscipleCount); BulletstoGo := Ammo; end; Procedure TChild2.Decease; begin EternalBurn; end; Procedure TChild2.Phone(Who: Caller); begin Case Who of AFT : Ventriloquize; Media : Say('Burp'); end; end; { In the next fragment all three Types of instances are put into a collection. } Var Christians : PCollection; begin Christians := New(PCollection, Init(2,1)); With Christians^ do begin Insert(PParent, Init('Mary')); Insert(PParent, Init('John')); Insert(PChild1, Init('Jesus', 12)); Insert(PChild2, Init('Koresh', 80, 1000000)); end; { Now you can have all instances pray ... } Procedure DoPray(Item: Pointer); Far; begin { unTyped Pointers cannot have method tables. The PParent Typecast Forces a lookup of Pray in the method table. All instances With a TParent ancestor will point to the same (non-Virtual) method } PParent(Item)^.Pray; end; { being sure all Items in Christians are derived from TParent } Christians^.ForEach(@DoPray); { and because all mortals will die... } Procedure endVisittoEarth(Item: Pointer); Far; begin { Decease is a Virtual method. The offset of a location in the VMT With the address of a Virtual method is determined by the Compiler. At run-time, For each Type of instance 1 VMT will be created, it's method-fields filled With the appropriate addresses to call. Each instance of an Object derived from TParent will have the address of it's VMT at the same location. Calling a Virtual method results in 1) retrieving that VMT address at a known offset in the instance's data structure 2) calling a Virtual method at a known offset in the VMT found in 1) ThereFor mr. Koresh will go to hell: PChild2's VMT contains at the offset For Decease the address of the overridden method. Mr. Jesus, a PChild1 instance, simply inherits the address of PParent's Decease method at that offset in the VMT. } PParent(Item)^.Decease; end; Christians^.ForEach(@endVisittoEarth); -> ...I've no problem posting my code, but I'm still not Really happy -> With it's present Implementation. I also don't think that dynamic -> Array Objects are very good examples of OOP. (For example, what -> do extend the dynamic-Array Object into, via inheiritance???) -> -> ...Something more like a generic "Menu" or "Line-Editor" Object -> might be a better example. Well I don't know exactly what you are trying to do With your dynamic Array but it can be OOP'ed. Linked lists are a prime example (I hope this is close) By using OOP to Write link lists you can come up with Objects such as: Type ListPtr = ^List; NodePtr = ^ListNode; List (Object) TNode : Pointer; {Pointer to the top Record} BNode : Pointer; {Pointer ro the bottom Record} CurNode : Pointer; {Current Pointer} Constructor Init; {Initializes List Object} Destructor Done; Virtual; {Destroys the list and all its nodes} Function top (Var Node : ListNode) : NodePtr; Function Bottom (Var Node : ListNode) : NodePtr; Function Next (Var Node : ListNode) : NodePtr; Function Prev (Var Node : ListNode) : NodePtr; Function Current(Var Node : ListNode) : NodePtr; Procedure AttachBeFore (Var Node : ListNode); Procedure AttachAfter (Var Node : ListNode); Procedure Detach (Var NodePtr : Pointer); end; ListNode = Object; Prev : NodePtr; Next : NodePtr; Constructor Init; Destructor Done; Virtual; end; The list Object is just that. It has the basic operations you would do with a list. You can have more than one list but only one set of methods will be linked in. The List node Dosn't have much other than the Pointers to link them into a list and an Init, done methods. Sounds like a ton of work just to implement a list but there is so much you can do easely With OOP that you would have a hard time doing conventionally. One example, because the ListNode's Done Destructor is Virtual the Done of the list can accually tranvirs the list and destroy all Objects in the list. One list can accually contain Objects that are not the same!!! Yep it sure can. As long as an Object is dirived from ListNode the list can handel it. Try to do that using conventional methods!! I'm assuming that your dynamic Array will do something similar which is why I suggested it. A Menu and Line editor Objects are High level Objects that should be based on smaller Objects. I'd assume that a line editor would be a Complex list of Strings so the list and ListNode Objects would need to be built. See what I mean??? then you get into Abstract Objects. These are Objects that define common methods For its decendants but do not accually have any code to suport them. This way you have set up a standard set of routines that all decendants would have and Programs could be written using them. THe results of which would be a Program that could handel any Object based on the abstract. -> RM>I have mixed feeling on this. I see OOP and Object as tools For a -> RM>Program to manipulate. -> -> RM> IE: File Objects, Screen Objects, ect then bind them together -> RM> in a Program using conventional style coding. -> -> ...to my understanding of the OOP style of Programming, this would -> be a "NO-NO". OK well With the exception of TApplication Object in Turbo Vision a Program is a speciaized code that more than likely can't be of any use For decendants. That was my reasioning at least. and the Tapp Object isn't a Program eather. YOu have to over ride a ton of methods to get it to do anything. Unit OpFile; {******* Capture this For future referance *******} Interface Type DateTimeRec = Record {Define the fields you want For date and time routines} end; AbstractFile = Object Function Open : Boolean; Virtual; {Opens the File in the requested mode base on internal Variables } {Returns True if sucessfull } Procedure Close; Virtual; {Flush all buffers and close the File } Function Exists : Boolean; Virtual; {Returns True is the File exists } Function Create : Boolean; Virtual; {Will create the File or overWrite it if it already exists } Procedure Delete; Virtual; {Will delete the File. } Function Rename : Boolean; Virtual; {Will rename the File returns True if successfull } Function Size : LongInt; Virtual; {Returns the size of the File. } Procedure Flush; Virtual; {Will flush the buffers without closing the File. } Function Lock : Boolean; Virtual; {Will attempt to lock the File in a network enviroment, returns } {True if sucessfull } Procedure Unlock; Virtual; {Will unlock the File in a network enviroment } Function Copy (PathName : String) : Boolean; Virtual; {Will copy its self to another File, returns True is successfull.} Function GetDateTime (Var DT : DateTimeRec) : Boolean; Virtual; {Will get the File date/time stamp. } Function SetDateTime (Var DT : DateTimeRec) : Boolean; Virtual; {Will set the File date stamp. } Function GetAttr : Byte; Virtual; {Will get the File attributes. } Function SetAttr (Atr : Byte) : Boolean; Virtual; {Will set a File's attributes. } end; {of AbstractFile Object} Implementation Procedure Abstract; {Cause a run time error of 211} begin Runerror (211); end; Function AbstractFile.Open : Boolean; begin Abstract; end; Procedure AbstractFile.Close; begin Abstract; end; Function AbstractFile.Exists : Boolean; begin Abstract; end; Function AbstractFile.Create : Boolean; begin Abstract; end; Procedure AbstractFile.Delete; begin Abstract; end; Function AbstractFile.Rename : Boolean; begin Abstract; end; Ok theres a few things we have to talk about here. 1. This is an ABSTRACT Object. It only defines a common set of routines that its decendants will have. 2. notice the Procedure Abstract. It will generate a runtime error 211. This is not defined by TP. Every Method of an Object has to do somthing. if we just did nothing we could launch our Program into space. By having all methods call Abstract it will error out the Program and you will know that you have called and abstract method. 3. I'm sure some may question why some are Procedures and some are Functions ie Open is a Function and close is a Boolean. What I based them on is if an error check a mandatory it will be a Function Boolean; This way loops will be clean. Open in a network Open will require a check because it may be locked. Which brings up point 4. 4. We are not even finished With this Object yet. We still have to define a standard error reporting / checking methods and also lock loop control methods. not to mention some kind of common data and methods to manipulate that data. Moving to point 5. 5. Where does it end??? Well we hvae added quite a few Virtual methods While thsi is not bad it does have a negative side. All Virtual methods will be linked in to the final EXE weather it is used or not. There are valid reasions For this but you don't want to make everything Virtual if it Dosn't have to be. My thinking is this. if it should be a standard routine For all decendants then it should be Virtual. if required methods call a method then why not make it Virtual (this will become more apparent in network methods and expanding this Object) Now personally I get a feeling that the DateTime and Attr methods shouldnn't be there or at least not Virtual as the vast majority of Programs will not need them and its pushing the limits of Operating system spisific methods. SO it will probly be a Dos only Object. (Yes there are others that have this but I think its over kill) The same goes For the copy and rename methods so I would lean to removing them from this Object and define them in decendants. So what do you think we need to have For error checking / reporting methods??? Do you think we could use more / different methods??? { DW> I am trying to teach myself about Object orientated Programming and DW> about 'inheritence'. This is my code using Records. The idea of Object oriented Programing is what is refered to as encapsulation. Your data and the Functions that manipulate it are grouped together. As an example, in a traditional Program, a linked list would look something like: } Type Linked_List = Record Data : Integer; {Some data} Next : ^Linked_List; {Next data} Prev : ^Linked_List; {Prev data} end; then you would have a whole slew of Functions that took Linked_List as a parameter. Under OOP, it would look more like Type Linked_List = Object Data : Integer; Next : ^Linked_List; Prev : ^Linked_List; Constructor Init(); {Initializes Linked_List} Destructor DeInit(); {Deinitializes Linked_List} Procedure AddItem(aData : Integer); Procedure GetItem(Var aData : Integer); end; then, to add an item to a particular list, the code would look like: This_Linked_List.AddItem(10); This is easier to understand. An easy way to think about this is that an Object is an entity sitting out there. You tell it what you want to do, instead of calling a Function you can't identify. Inheritance allows you to make a linked list that holds a different Type, but Uses the same Interface funtions. More importantly, using the same method and Pointers, you could have both Types in the same list, depending on how you implemented it. It helps debugging time, because if you wanted to add a Walk_List Function, you could add it and get it working For the parent Object, and (since the mechanics of it would be the same For ANY Linked List), you could Write it once and use it without problems. That is a clear advantage. Other Uses include: (For a door Type Program) and Input/Output Object that serves as a base For a console Object and a modem Object, and thusly allows you to treat the two as the same device, allowing you to easily use both. (For a BBS Message base kit) a Generic Message Object that serves as a base For a set of Objects, each of which implements a different BBS' data structures. Using this kit, a Program could send a message to any of the BBSes just by knowing the core Object's mechanics. (For Windows) a Generic Object represents a Generic Window. By inheritance, you inherit the Functionality of the original Window. By creating an Object derived from the generic Window, you can add additional Functionality, without having to first Write routines to mirror existing Functionality. (For Sound) a Generic Object represents a generic Sound device. Specific child Object translate basic commands (note on, note off, etc) to device specific commands. Again, the Program doesn't have to know whether there is a PC speaker or an Adlib or a SoundBlaster--all it has to know is that it calls note_on to start a note and note_off to end a note. There are thousands on thousands of other examples. if you read through the turbo guides to turbovision or to Object oriented Programming, they will help you understand. Also, a good book on Object oriented Programming doesn't hurt ;>. { > Now, the questions: > 1. How do I discretly get the Lat & Long into separate > Collections? In other Words (psuedocode): No need For seperate collections, put all the inFormation in a Single collection. > Any hints would be appreciated. Thanks! I'll not give any help With parsing the Text File, there will probably be a ton of advice there, but here is a little Program that I threw together (and tested) that will list the inFormation and present the additional data. Have fun With it. } Program Test; Uses Objects,dialogs,app,drivers,views,menus,msgbox; Type (*Define the Data Element Type*) Data = Record Location : PString; Long,Lat : Real; end; PData = ^Data; (*Define a colection of the data elements*) DataCol = Object(TCollection) Procedure FreeItem(Item:Pointer); Virtual; end; PDC =^DataCol; (*Define a list to display the collection*) DataList = Object(TListBox) Function GetText(item:Integer;maxlen:Integer):String; Virtual; Destructor done; Virtual; end; PDL = ^DataList; (*Define a dialog to display the list *) DataDlg = Object(TDialog) Pc : PDC; Pl : PDL; Ps : PScrollBar; Constructor Init(Var bounds:Trect;Atitle:TTitleStr); Procedure HandleEvent(Var Event:TEvent); Virtual; end; PDD = ^DataDlg; Const CmCo = 100; CmGo = 101; Procedure DataCol.FreeItem(Item:Pointer); begin disposeStr(PString(PData(Item)^.Location)); dispose(PData(Item)); end; Function DataList.GetText(item:Integer;maxlen:Integer):String; begin GetText := PString(PData(List^.At(item))^.Location)^; end; Destructor DataList.Done; begin Dispose(PDC(List),Done); TListBox.Done; end; Constructor DataDLG.Init(Var bounds:Trect;Atitle:TTitleStr); Var r : trect; pd : pdata; begin TDialog.Init(bounds,ATitle); geTextent(r); r.grow(-1,-1); r.a.x := r.b.x - 1; dec(r.b.y); new(ps,init(r)); insert(ps); geTextent(r); r.grow(-1,-1); dec(r.b.x); dec(r.b.y); new(pl,init(r,1,ps)); insert(pl); geTextent(r); r.grow(-1,-1); r.a.y := r.b.y - 1; insert(new(pstatusline,init(r, newstatusdef(0,$FFFF, newstatuskey('~[Esc]~ Quit ',kbesc,CmGo, newstatuskey(' ~[Alt-C]~ Co-ordinates ',kbaltc,CmCo, newstatuskey('',kbenter,CmCo,nil))),nil)))); new(Pc,init(3,0)); With pc^ do (*parse your File and fill the*) begin (*collection here *) new(pd); pd^.location := newstr('Port Arthur, Texas'); pd^.long := 29.875; pd^.lat := 93.9375; insert(pd); new(pd); pd^.location := newstr('Port-au-Prince, Haiti'); pd^.long := 18.53; pd^.lat := 72.33; insert(pd); new(pd); pd^.location := newstr('Roswell, New Mexico'); pd^.long := 33.44118; pd^.lat := 104.5643; insert(pd); end; Pl^.newlist(pc); end; Procedure DataDlg.HandleEvent(Var Event:TEvent); Var los,las : String; begin TDialog.HandleEvent(Event); if Event.What = EvCommand then Case Event.Command of CmGo : endModal(Event.Command); CmCo : begin str(PData(Pl^.List^.At(Pl^.Focused))^.Long:3:3,los); str(PData(Pl^.List^.At(Pl^.Focused))^.Lat:3:3,las); MessageBox( #3+PString(PData(Pl^.List^.At(Pl^.Focused))^.Location)^ + #13+#3+'Longitude : '+los+#13+#3+'Latitude : '+las, nil,mfinFormation+mfokbutton); end; end; end; Type (*the application layer *) myapp = Object(Tapplication) Procedure run; Virtual; end; Procedure myapp.run; Var r:trect; p:PDD; begin geTextent(r); r.grow(-20,-5); new(p,init(r,'Dialog by ken burrows')); if p <> nil then begin desktop^.execview(p); dispose(p,done); end; end; Var a:myapp; begin a.init; a.run; a.done; end. > I am having a problem. I would like to Write an editor. The > problem is I dont understand a thing about Pointers (which everyone > seems to use For editors). I'm certainly no TP expert, but I might be able to help out With the Pointers. Pointers are just special 4-Byte Variables that contain ( point to) a specific position in memory. You can also make a Pointer act like the thing to which it is pointing is a particular Type of Variable (Byte, String, etc). Unlike normal Var Variables, however, these Variables are what's referred to as Virtual -- they aren't fixed in the .EXE code like Var Vars, so you can have as many of them as you like, within memory Constraints. Each is created when needed using the GetMem statement. This statement makes a request For some more memory to be used in the heap (all left-over memory when the Program loads usually). What you need in a editor is to be able to somehow link the Strings that make up the document into what's called a list (first line, next, ... , last line). The easiest way to visualize this is a bunch of people in a line holding hands, each hand being a Pointer. The hand is not the entire person, it just connects to the next one. So, what you do is use a Record that contains one String For one line of Text, a Pointer to the previous line of Text in the document, and a Pointer to the next line in the document. A Record like this should do it: {+------------------------- Usually used in starting a Type of Pointer} {|+------------------------ Points to a String in the document } {|| +----------- This is usedto mean that PStringItem is } || | to be a Pointer pointing to a Record } || | known as TStringItem } {vv v Type PStringItem = ^TStringItem; TStringItem : Record LineOText : String [160]; {Double the screen width should do it} NextLine : PStringItem; {Points to the next line in memory} PrevLine : PStringItem; {Points to the previous line in memory} end; In your editor main Program, use Var FirstLine, LastLine, StartLine, CurrLine : PStringItem; to create Varibles giving you `bookmarks' to the first line in the File, last in the File, the one the cursor is on, and the one that starts the screen. All of these will change. to create the first line in the document, use: GetMem (FirstLine, Sizeof (TStringItem)); {get memory enough For one line} CurrLine := FirstLine; {of course, only one line in the doc so Far!} LastLine := FirstLine; StartLine := FirstLine; FirstLine^.NextLine := nil; {nil means no particular place-- there's no} FirstLine^.PrevLine := nil; {line beFore of after FirstLine yet } Now the Variable FirstLine will contain the address of the newly created Variable. to address that Variable, use the carrot (^), like this: FirstLine^.LineOText := 'Hello World!'); to make a new line in the list just get more memory For another line: GetMem (LastLine^.NextLine, Sizeof (TStringItem)); LastLine := LastLine^.NextLine; This will get more memory and set the last line in the File's next line Pointer to the new String, then make the new String the last line. Deleting a line is almost as simple. You use the FreeMem Procedure to release the memory used by a Variable. if it's in the middle of the list, just set the to-be-deleted's next line's previous line to the to-be deleted's previous line, and the previous line's next to the one after the one to be deleted, essentially removing it from the list and then tieing the peices back together. You can then kill off the memory used by that line. {Delete current line} if CurrLine^.NextLine <> nil then {there's a line after this one} CurrLine^.NextLine^.PrevLine := CurrLine^.PrevLine; if CurrLine^.PrevLine <> nil then {there's a line beFore this one} CurrLine^.PrevLine^.NextLine := CurrLine^.NextLine; FreeMem (CurrLine, Sizeof (TStringItem)); to insert a line, just do about the opposite. if you don't understand, I won't blame you, I'm half asleep anyway... but I hoe it clears some of the fog. if the manual isn't helpful enough now, try tom Swan's _Mastering Turbo Pascal_, an excellent book.