[<<Previous Entry] [^^Up^^] [Next Entry>>] [Menu] [About The Guide]

  OOPS - The big easy, or 'who the hell is SELF ??'         


  Although this is not the tutorial part of the guides, I am sure that a
  little introduction into the wonderful world of OOPs can't hurt. If you
  are already familiar with this topic, please feel free to skip this
  section.

  For me, knowing about the underlying mechanism was the key to understand
  the concept of OOPs, so I will illuminate the subject from the more 
  technical and practical side.


  The first thing to learn for an OOPs Newbie is to tell the difference
  between an Object and a Class, because it is one of the most common errors
  to mix them up.

  The basical instruments of OOP are classes and messages, not objects.
  The Class is a template, or blueprint, for the Objects it creates. It 
  can build an unlimited number of Objects, which will all be identical
  until you initialize them with different data.
  Classes are defined with the CREATE CLASS command, and the resulting
  Function from that Definition is called the Class Function. It's Name is
  the Classname ( like TWINDOW ), and it  is a kind of DBCREATE(), but
  instead of creating a table it generates Objects, according to the given
  specification.

  The Object itself looks like an Array; all Variables ( called Instance-
  Variables in OOP ) are elements of this Array. Connected to this Object is
  a hidden MemoryPart, where special informations are stored, like the
  Classname of the Class ( which can be retrieved for every Object by using
  the Object:ClassName message ), and the Functions ( called Methods in
  OOP ) that are available for this Class.

  So let us summarize : Classes are functions that create objects, according
  to their definition. Objects send and receive messages that control the
  program flow.
  
  What? sending messages? 

  Yes, it sounds like a post office, but it's far more efficient <g>. 
  Inside Clipper, every Symbolic text ( called WEED ), like a Function name
  or a Message, is associated with a SYMBOL structure. 
  What happens, when you use

    +------------------------------------------+
    |      oWin:SetFocus()                     |
    +------------------------------------------+

  which reads like 'sending the message "SETFOCUS" to the Object oWin' ?

  First the message(-string) "SETFOCUS" is translated into a pointer to
  the Adress where the Symbol "SETFOCUS" is stored. From now on, our message
  is a numeric value, which is much easier to handle than a string.

  Next, the SendModule is activated, which retrieves to which class oWin
  belongs, and if this class knows the message SETFOCUS at all.
  This is done with a 'hashtable' of the class, in which all messages are
  listed as a Symbol ( See the function nStrhash() for more informations
  on Hashing ). 
  Once the entry is found, we have the adress of the function or variable
  that is associated to the message, and access to other Infos, like the
  determined type of instance var, or visibilitiy of the method.
  
  This additional layer of abstraction between the (alias)name of a function
  and it's physical adress is the reason why a Function can be named Hj_p(),
  but may be called with the more meaningful name o:SetFocus(). In Fact, we
  can have one functions SetFocus in every class, without running into
  problems with duplicated functions. Or we can have two message names for
  one Method, like in :

    +------------------------------------------+
    |  MESSAGE Setfocus METHOD WinFocus_()     |
    |  MESSAGE Activate METHOD WinFocus_()     |
    +------------------------------------------+

  Both messages will call the same function - oops, sorry, Method. With
  FiveWin you will notice that nearly every process can be terminated with
  the "END" message - the same Message will launch different actions,
  according to the object that you send the message to. This is one of the
  key features of OOPs, and in our own slang, we call this PolyMorphism.


  return to sender 

  If you have problems with the concept of the send operator ':', then let
  me explain it using clipper pseudo code. Imagine that the Object oTable is
  an Array with the structure { cMessage, xVar, lIsVar } :
                  

    +--------------------------------------------------------------+
    |  /* Defining the pseudo Class TABLE */                       |
    |  otable:= {{ "AREA" ,            0, .T. },;                  |
    |            { "ALIAS",       "Cust", .T. },;                  |
    |            { "OPEN" , {|o|open(o)}, .F. } }                  |
    +--------------------------------------------------------------+


  Where Area and Alias are Instance Vars, and Open is a Method of oTable.
  For our example, we assume that Clippers great PreProcessor is programmed
  to translate 'oTable:area := 1'  Into  'Send( otable, "AREA", 1 )'

  So then our SendModule SEND() will search the "Object" oTable for the
  message "ALIAS", and then replaces the associated Variable with the 
  argument 1. 
  If we send the Message "oTable:OPEN" instead, our SendModule will be smart
  enough to see that OPEN is a Method ( lIsVar == .F. ), and therefore evals
  the associated codeblock. As an argument, it passes the "Object" to the      
  block and its embedded "Method".

  Ok so far ?

  Now, inside this method, we don't need to specify oTable. Our Table Object
  is always passed to the method invisible as parameter 0 ! To access oTable
  from inside the Method, FiveWin generates code like this for every Method:


    +-------------------------------------------------+
    |  /* METHOD DEFINITION */                        |
    |                                                 |
    |  METHOD <xyz> [CLASS <Classn>] ;                |
    |                => ;                             |
    |                FUNCTION <xyz> ;                 |
    |                   LOCAL self := QSELF()         |
    |                                                 |
    +-------------------------------------------------+


  Qself retrieves the Object, and stores it to the local var SELF. Because
  we use SELF rather often, we trasnlate a double colon to SELF:   


    +-------------------------------------------------+
    |  /* ShortCutting SELF */                        |
    |  #xtranslate ::  =>  Self:                      |
    +-------------------------------------------------+

  This will save us a lot of typing. You see: inside our Method the Message  
  ::Open() is the same as Self:Open(),  which is the same as
  QSelf():Open(), its just shorter and faster than the later.

  So, if you are still uncomfortable with the 'Method' terminus, just think
  of it as a static function that grabs a hidden parameter, and gets called
  indirectly by our SendModule.  Feel better now ?   

  
  But what does this thing called 'Inheritance' mean ?

  If we stay with the Array model from above, the Inheritance would simply
  mean that our new "Class Access FROM Table" will combine the TABLE class
  Array with the new Access-class Array. Now our sendModule finds all
  messages from both classes in one object !
  
  The real benefit of this is the abiltiy to 'change' the behaviour ( the 
  methods ) of the parent class, without actually changing the code of that
  that method  ( in fact, sometimes even without knowing the code ! )

  Imagine that you have 8 heavily used functions, that are interfaced with 
  each other. Now you have to teach them new tricks, adding 2 functions; 
  6 of the old functions are working perfectly with little or no change, but
  the remaining 2 functions don't, so you have  to get 'inbetween' them to
  intercept some things and fiddle some IF- ENDIF - DO CASE crutches into
  the existing code - normal problems.
  And normally you will break your code with the 'little changes', which
  results in extensive debugging and new testing. 

  Now the same thing in OOP: You create a new Class "Newbie", that inherits
  from the 8-Method class "Oldie". Newbie has four Methods : two new ones, 
  and two replacement methods for the Oldie methods that make all the 
  trouble in the upper example !

  How goes this without changing the code ? You just define a Method with
  the same (Message)Name, as the original Method that you want to replace ! 

  Because in OOP's functions are called with a Message, rather than by their
  function adress, all of the methods of your Newbie class ( also the 'old',
  inherited methods ) will now automatically call your new Method.
  This does not mean that the original 'overwritten' method is no more
  accessible from the Newbie class: Just use the 'SUPER' or 'PARENT' message
  to explicietly call the underlying method.

  So all you got to do is programm the changes into the Newbie methods, and
  then let the Oldie methods do the rest. This is why OOP is often called
  "Programming the Exception" : you end up by coding only the Exceptions
  into the new class, and build up from the already code. This makes slim 
  code, and less debugging and retesting, because you don't need to touch
  the underlying code.

  Got the Idea now ? 

  If not, don't worry. Go ahead and try it out :'let your fingers float, and
  your mind will follow'. OOPs is, technically seen, a very simple thing.
  It's more a way of thinking, and its a one way street: Once you have
  crossed the magical line, you will never ever want go back to procedural
  programming.


  Oh yes, one for the way:

  If you want to read a real good book about OOPs, I can strongly recommend
  'MENTOPOLIS' by Marvin Minsky, ISBN 3-608-93117-1. Don't expect to read
  anything about Computers or -Languages in it: Its a pure Analysis of the
  human way of thinking and learning, from an OOPs side of view. But it is
  one of the most fascinating and exacting book I have ever read.
   



See Also: nStrhash
This page created by ng2html v1.05, the Norton guide to HTML conversion utility. Written by Dave Pearson