Retro video games delivered to your door every month!
Click above to get retro games delivered to your door ever month!
X-Hacker.org- The Guide to Clip-4-Win version 3.0 - http://www.X-Hacker.org [<<Previous Entry] [^^Up^^] [Next Entry>>] [Menu] [About The Guide]
  
  For Clipper with TopClass / VO, here's a simple, arguably
  contrived, class.  It's deliberately over-simple so that its
  shortcomings can be explored and remedied later.
  
       CLASS Money
            EXPORT    Amount    AS NUMERIC
  
            METHOD    Init(nAmount)  // not needed for VO
       ENDCLASS                      // not needed for VO
  
       METHOD Init(nAmount) CLASS Money
            self:Amount := nAmount
       RETURN self
  
  The above defines a class, i.e. a template for making objects,
  such that objects of the class Money would each have storage
  space for one numeric (the Amount instance variable), and have
  just one method, called Init.  You may prefer to use "nAmount"
  instead of "Amount" - if so, you can.
  
  The ":" operator is used to specify an instance variable or a
  method of the object which is the operator's left operand.
  The meaning of "self" is explained shortly.
  
  The two lines marked "not needed for VO" are needed in
  TopClass, but not VO.  You can treat them as helping to
  specify the interface of the class, if you like.  (As the
  examples get larger such comments will be omitted.)  Also,
  TopClass, like other Clipper 5 OO add-ons, allows you to
  shorten "self:" to "::", which is not currently supported by VO.
  
  To make some actual objects you use code like this:
  
       LOCAL     oTotal, oNew := Money{4.50}
  
  This means we have a variable oTotal, currently NIL, and a
  variable oNew, currently set to a Money object with its Amount
  set to 4.50.  How did that happen?  The syntax:
  
       <name> { [ <optional params> ] }
  
  means to make an object of class <name>, and make it available
  as "self" to the Init() method, passing any <optional params>.
  Notice that Init() carefully returns self.  When
  Init() is called you should initialise the object as fully as
  you can, so that when it's returned the object is in a
  consistent, usable state, as far as possible.
  
  To confuse you, there are thus three uses of braces ("{" and
  "}", often called curly brackets): for arrays, code blocks,
  and now object initialisers.  As if that's not bad enough,
  some font sizes make braces almost indistinguishable from
  parentheses ("(" and ")", also called round brackets).
  
  
  Let's suppose you really wanted a money class with a simple
  kind of currency awareness (full multi-currency handling is
  beyond the reach of this discussion).  You might decide to do
  this:
  
       CLASS Money
            EXPORT    Amount    AS NUMERIC
            EXPORT    Currency  AS NUMERIC
  
            METHOD    AsString()
            METHOD    Init(nAmount, nCurrency)
       ENDCLASS
  
       METHOD Init(nAmount, nCurrency) CLASS Money
            default nCurrency to DEFAULT_CURRENCY
            self:Amount := nAmount
            self:Currency := nCurrency
       RETURN self
  
       METHOD AsString() CLASS Money
       local     nCurrency := self:Currency,                ;
                 cAmt := ltrim(str(self:Amount)), cStr
       do case
       case nCurrency == DOLLARS
            cStr := "$" + cAmt
       case nCurrency == POUNDS
            cStr := "." + cAmt
       case nCurrency == FRANCS
            cStr := cAmt + "F"       // you also need to swap "," and "."
       // . . .
       endcase
       return cStr
  
  You can use the above class like this:
  
       local     oTotal := Money{4.50, DOLLARS}
  
       ? "Total is " + oTotal:AsString()
  
  
  Although it's encapsulated, one problem with the above is that
  it has a sequence of CASE statements for the currency type.
  An alternative is to use inheritance.  Here's some TopClass
  syntax, using INLINE.  Notice the use of polymorphism - each
  sub-class has its own AsString() method.
  
       CLASS Money
            EXPORT    Amount    AS NUMERIC
            METHOD    Init(nAmount)
       ENDCLASS
  
       METHOD Init(nAmount) CLASS Money
            self:Amount := nAmount
       RETURN self
  
       CLASS Dollars INHERIT Money
            METHOD AsString()  INLINE "$" + ltrim(str(self:Amount))
       ENDCLASS
  
       CLASS Pounds INHERIT Money
            METHOD AsString()  INLINE "." + ltrim(str(self:Amount))
       ENDCLASS
  
       CLASS Francs INHERIT Money
            // This still doesn't swap "," and "."
            METHOD AsString()  INLINE ltrim(str(self:Amount)) + "F"
       ENDCLASS
  
  
  Now you can do things like the following, and make use of the
  polymorphism:
  
       local     oUSD := Dollars{4.50}, oGBP := Pounds{3.00}, oMoney
       local     aAmounts := {oUSD, oGBP}, i
  
       for i =1 to len(aAmounts)
            oMoney :=  aAmounts[i]
            ? "Amount is " + oMoney:AsString()
       next i
  
  In this example, oMoney:AsString() will automatically select
  the AsString() method for whatever type oMoney is.
  
  Because of the way inheritance works, each of the sub-classes
  has an "Amount".  Also, there's no need to write a new Init()
  method for any of them, as the inherited one is fine.
  
  If you did need to write an Init() method, proceed as in the
  following rather silly example:
  
       CLASS Cents INHERIT Dollars
            METHOD Init(nAmount)
       ENDCLASS
  
       METHOD Init(nAmount) CLASS Cents
            self:Amount := nAmount / 100
       RETURN self
  
  In TopClass, this can be shortened to:
  
       CLASS Cents INHERIT Dollars
            METHOD Init(nAmount)                           ;
                   INLINE self:Amount := nAmount / 100,    ;
                          self
       ENDCLASS
  
  The comma (",") is needed because INLINE results in a code
  block.  Whether you like INLINE is up to you, but it's
  certainly faster.  Being a code block, its value is the value
  of the last expression - "self" in this example.
  
  Although contrived, the above examples show you how
  "programming by differences" (inheritance) lets you write code
  for just the things that have changed.
  

  

Online resources provided by: http://www.X-Hacker.org --- NG 2 HTML conversion by Dave Pearson