Plateforme Level Extreme
Abonnement
Profil corporatif
Produits & Services
Support
Légal
English
How can be read method code at runtime ?
Message
 
 
À
19/11/1999 13:53:54
Nancy Folsom
Pixel Dust Industries
Washington, États-Unis
Information générale
Forum:
Visual FoxPro
Catégorie:
Programmation Orientée Object
Divers
Thread ID:
00292948
Message ID:
00293301
Vues:
32
Nancy,

>But, you have to know where to put the DODEFAULT(), which means you have to walk up the inheritance tree, which (IMO) breaks encapsulation. You wouldn't agree that it is better to clearly delineate which class or object in the hierarchy *owns* the method?

You sometimes do have to have intimate knowledge of the class to properly place the dodefault(). It's not so much breaking encapsulation as it is peeking into the information hiding aspect of the class. Although a well documented class would let you know what it is doing, it doesn't have to tell you how it is doing it. This should be sufficient information to know whether you should pre/mid/post call dodefault. 98% of the time dodefault() is the first thing I do in an overridden method.

Even if a method is abstract in the class and requires the subclass to put code there, it doesn't say that your class hierarchy doesn't impliment code at a couple of methods along the tree. You shouldn't ever write code that makes hard assumptions about what is higher up (or maybe lower down) the class tree. There's no guarantees (other than instance level on a form object) where the class is in the tree.

I'm working now on a project to write a generic set of import classes. Part of the classlib looks like:
cObject
   cParseFile
      cSingleLineParseFile
         cCSVParseFile
         cSDFParseFile
      cMultiLineParseFile
         cEMailParseFile
The cParseFile class has several abstract methods CreateCursor(), Import(), VerifyImportStructure() and VerifyRawData(). It also has concrete methods Scatter(), DestroyCursor() and ReadImportFile(). The concrete methods take on two roles: 1) doing the grunt work every subclass needs, 2) making calls to the abstract methods in the correct order making them essentially controller methods. The DestroyCursor() method:
* close the cusor created by CreateCursor

if ( used( (this.cCursorName) ) )
   use in (this.cCursorName)
endif
Can easily get rid of the cursor created by CreateCursor(). There's no dodefault() call because this is the class that defines this method.

CreateCursor() must build the cursor to suit the particular needs of the specific import being done so it is abstract in cParseFile. This method is filled in at the instance level. But what if at some point in time cParseFile decided that it needs to create a generic cursor that all derived objects needed. If the instance, or subclass, didn't properly call dodefault() then the object will break because the generic cursor didn't get created. Sure another method could be added to create the cursor, but why pollute the namespace and interface with another method when we already have the perfectly suitable CreateCursor() method?

The controller method like ReadImportFile():
local lcFile, llRetVal

with this
   .cFilename = .parent.cFilename

   select (.cCursorName)

   llRetVal = .f.

   if ( .CheckPreviousImport() )
      lcFile = .GetImportFile()
      .cProgressTitle = 'Reading ' + lcFile

      if ( ! empty( lcFile ) )
         if ( .VerifyImportFileStructure( lcFile ) )
            .ReadImportFileToCursor( lcFile )
            goto top
            llRetVal = .VerifyRawData()
            if ( .lCheckPreviousImport )
               select count(*) ;
                  from (.cCursorName) ;
                  into array laJunk
               .parent.parent.nRawRec = laJunk[1]
            endif
         else
            .cErrorStatus = "File structure is bad:" + lcFile
         endif
       endif
   endif
endwith

return llRetVal
Is not designed (or desirable) to be overridden, There is lots of conditional code in these kind of methods which makes it a bitch to override and know where to dodefault() to it. To make these controller methods workable for subclasses though plug in abstract/concrete method calls are made: CheckPreviousImport(), GetImportFile(), VerifyImportFileStructure() and VerifyRawData(). These allow the subclasses to impliment the VerifyImportFileStructure method which is vastly different between a CSV and a SDF file for example. These methods are provided by the cCSVParseFile and cSDFParseFile classes, and the instance level doesn't need any code there. Finding the right plug in points sometimes takes actually building a couple of subclasses to find deficiencies in these controller methods. I'm not sure all of the design time in the world would necessarily help some of this is just an evolutionary/iterative process, at least it is for me.

The cSDFParseFile.ReadImportFile():
lparameter lcFile

dodefault()

this.cProgressTitle = "Loading file " + lcFile

append from (lcFile) type sdf
goto top
if ( this.nHeaderLines > 0 )
   * get rid of header lines
   delete next this.nHeaderLines
endif
Does do a dodefault() because the method is defined two levels up the class tree and even though there's no code higher up right now, it doesn't mean that there won't be code there someday.

I think I've diatribed on long enough... *g* if something doesn't make sense or needs more discussion holler.
df (was a 10 time MVP)

df FoxPro website
FoxPro Wiki site online, editable knowledgebase
Précédent
Suivant
Répondre
Fil
Voir

Click here to load this message in the networking platform