Version: 1.00.10 - Last Update: Sunday, September 27, 2009, 16:50:00
This thread is all about OOP BASICS
Last additions/modifications are marked with
Please notice: This is a work in progress!
I made the experience that many smart VFP guys out there don't know the whole OOP-story. I mean, they are pretty good in creating powerful classes, but they fail to explain what the real difference is between object behaviour and appearance. Do you?
According to my own OOP design rules all my classes have beside their native INIT() and DESTROY() methods at least additional START() and STOP() methods.
VFP's native classes don't have real constructor and destructor methods we could use to overwrite with our own improvements.
At the moment VFP passes control back to our code within the INIT() method, VFP's internal object construction was finished! VFP's INIT() method is something like a post-construction hook-function. Thus, it should only be used to help VFP to finalize its object construction in that the only code we might put there is something that dimensions arrays and fills properties with the right default values for example. When it comes to real encapsulation - I mean the real art of coding reusable classes - we have to reassure that every class instance is fully encapsulated just after its initialization is finished.
What is a proof of full encapsulation?
Well, full encapsulation means that at the unique "post-init" point in your object's lifecycle all its properties have to match the type and value (you coded in your class definition) exactly . I name that object appearance (object appearance:=the values of all object properties) the post-init appearance or initial appearance. Within this initial context no data or information that doesn't already exist inside your object should be assigned to any of its properties. In other words: If you intend to create fully encapsulated classes, you never must write class code that assigns any external value to a property before the class instance's INIT() returned. Never write INIT-code in your reusable classes that presumes something to be existent (or even predictable) outside your object at runtime at this early stage.
BTW: Go and have a look at VFP's own online help about topic: "Init event". You can read the following there:
To prevent a control from being created, return false (.F.) from the INIT`() event. The Destroy event will not be triggered. For example, the following code returns false (.F.) if the Invoice table is not available:
PROCEDURE INIT IF NOT FILE("INVOICE.DBF") ERROR 'Initialization Failed: File not found' RETURN .F. ELSE USE INVOICE IN 0 AGAIN THIS.WorkArea = SELECT() ENDIF ENDPROC
Well, those guys are not only breaking all (my) rules of encapsulation there, they're also doing another big no-No-NO: they're returning a FALSE from INIT(). Doing so is a real evidence of incapacity to me! Why?
1st: The information FILE("INVOICE.DBF") definitely is external to the object at that time. Not only at that special INIT()-point in object's lifetime, but at almost every point in time – especially, when we are coding the class. Thus, the information about the existence of a dBase file called "Invoice.dbf" is a first class citizen of object-external information!
2nd: Another big point to know (and to live and to die for) is that any object in memory has the “damn right" to exist as long as all of its properties are valid! This is the OOP concept of object validity in contrast to object utility.
3rd: Every programmer should be able to write classes from which valid objects can be created at runtime - at least, encapsulated objects with a valid post-init state. Otherwise the class concept can be considered nothing but trash.
IMHO and according to my own OOP design rules, the VFP HELP crew must have had a class in mind that should be able to create valid instances of objects. Well, if no invoice table exists in VFP’s FILE()-function's search path the object isn't useful (seen from the perspective of other objects running in the same context). But, that must not hinder the object from staying in memory! Who knows, maybe the table in question will become available later!?
22 OOP Design Rules
Beside the idea to write a separate documentation about good (and poor) OOP design, here are 22 of my basic OOP design and development rules. I hope you will find them useful. At least this chapter can help you understand my implementations later ;-)
The classes of my framework I'm going to talk about are fully reusable/encapsulated. The rules I'm going to enumerate below apply to that kind of reusable classes. Naturally, there are exceptions to my rules, we will have to talk about that later.
Another very good source of information I would like to point you to Christof Wollenhaupt's Safe Base Classes document. Christof shows us a code-based approach to write bullet-proof (base) classes. Contrary to that, my own OOP rules show you a way less carved in stone; they seem to be more liberally to me. Maybe you tend to implement a mixture of both approaches. The outcome always should be the same. A good class design will always lead to bullet-proof class hierarchies that will instantiate stable running, well behaved objects at runtime.
Rule #01: If avoidable, never initialize static class fields dynamically!
I must admit, this rule sounds strange. Thus, let me translate: A property is also called a static class field - so far, so good. We all know that we can write something like the following in VFP's property editor: ="My new Caption"
This is called a dynamic assignment, even if my example is a pretty simplistic one. We could create other assignment including function calls like this: = SYS(2015) The SYS(2015) function returns a unique procedure name. It sounds to be the perfect fit for attaching a unique ID to every class instance. Check it out - you will find that every instance you create has the same SYS(2015)-value! Strange? Well, not if you've read Christof's document (linked above). In fact, you are assigning the SYS(2015) result to VFP's internal class object only the first time you instantiate an object from that class! All successively created objects won't evaluate the SYS(2015) call again! VFP fetches the buffered value of its class object from its internal class cache instead! Thus, if you are not absolutely safe as houses about what you're doing, do not touch VFP's internal class object properties this way. Rule#01 leads us to the next rule of thumb...
Rule #02: Always use your objects' INIT() method to assign class field default values!
Now, this is pretty easy to grasp. The moment VFP hands over control to our INIT() method, the internal class object is fully created and all properties are initialized with the class defaults. From now on, when you write to your object properties, you can be sure you are writing to your current class instance and not VFP's class object. Thus, assigning some unique value using =SYS(2015) to a property from there will always yield an unique ID inside your property - as expected. BTW: As we will see in Part II its always a good idea to implement things like the property initialization and others in a discrete self-defined method and call that method from Init() or Destroy(), instead of putting all code in VFP's native methods!
Rule #03: If avoidable, do not pass information into VFP's INIT() methods!
There are plenty of good reasons for not using input parameters within VFP's INIT() methods. One of the more obvious ones is, that you might want to reuse your class instantiated inside another container later in the game. Within such a construct the INIT() of your inner object will fire before the INIT() of your outer/containing object and that's not what you want, because there will be no parameter-data available then! Another (OOP-design) reason is, that passing external information into your object at this early stage (before INIT() has finished!) breaks full object encapsulation (see below)! Data passed into an object always is something external seen from the object's point of view. Thus, don't start passing in external information before you're done with your internal (self-contained) initialization!
Lately, someone who noticed that my own classes do have an INIT() input parameter, asked me why I’m breaking my own rules! :-) Let me tell you, the only input parameter I do accept in all INIT() methods of my reusable super-classes looks like this: LPARAMETERS tlAutoStart AS BOOLEAN. The <tlAutoStart>-value is no external data (in terms of “something vitally important I must know”), but solely used as workflow control information. If <tlAutoStart> is TRUE, the object gets started automatically (Start() gets called from Init()). Otherwise, Init() finishes and control is passed back to the parental maker, which then may or may not start the object (sooner or later). Therefore, <tlAutoStart> is no external information, but belongs to object’s internal process control data, merely introduced at Init() dynamically.
Rule #04: Always try to achieve full object encapsulation!
The best reason to haunt for full object encapsulation is that this is the only bullet-proof way to achieve full class reusability. Full encapsulation is pretty easy to achieve. 1st: Never access any information outside your object boundaries from within your class instance. 2nd: Never pass in any information before your INIT() method was successfully completed (returned TRUE)
Rule #05: Go for strongly typed properties
VFP doesn't support strongly typed properties. It's up to you to fill this gap! Why? Because its the only way to find the correct answer to the question "Is this class instance valid?" Why should we ask this? Because any valid object should have the established right to stay in VFP's working memory! Only if an object renders invalid it must be removed (or repaired)! Strongly typed properties are one major building block of error-free, thus excellent, class design. The crucial question is: How can we implement such a feature transparently? The answer to this question is: USE _ASSIGN() and _ACCESS() methods (see below)!
Rule #06: Go for property validation
We can check the type of any property pretty easily using _ASSIGN() and _ACCESS() methods. But that's not enough to let you know if a property is valid! In addition to that you need to know the range of valid values the property may hold! Only if a property value has a given type AND is inside the valid range, the property is valid! Thus, we have to implement a pretty complex strategy to simulate strong typing!
Rule #07: Go for object validation
We know by now what a valid property looks like. Now we can expand this definition to fit for an entire object. If all properties of an object are valid, then (and only then!) the entire object is valid. If only one property renders invalid, the whole object will become invalid. An invalid object isn't allowed to stay in VFP's working memory!
Rule #08: Get the difference - Dynamic vs. Static class fields
Dynamic class field is another generalized term for methods. Static class fields are properties (we've learned). Some programming languages implement a feature called uniform access principle. VFP doesn't support this natively. The principle of accessing class field information uniformly is realized (for example in Eiffel) by using the same notation when getting class data either by querying a property directly or by calling a non-parameterized function. In VFP we have to distinguish between a property query and a function call syntactically - the latter uses parenthesis. The uniform access principle makes it easier for the reusing developer to access object information, because she doesn't have to know how the class field was implemented internally. Thus, there is an even more generalized categorization (see next rule).
Rule #09: Get the difference - Queries vs. Commands
Queries are all kinds of questions you can ask an object's interface. A query always returns a value (not two values or even more, but only one value!). In other languages (like Eiffel) the reusing developer doesn't have to know how an object query is implemented (thanks to the uniform access principle). Although, every VFP developer has to be aware of the correct type: is it a static query, then she will access an object's property. Is it a dynamic query, then she calls a non-parameterized function that returns a value as well. A dynamic query never changes the object's appearance! If a dynamic query fails it can signal it's own failure by returning a predefined error value (see query contracting)
Commands are all other object's methods that are no queries or events. In contrast to queries a command never returns any value but can change the object's appearance (and of coarse it's state, too). The only way a command can signal a broken contract is to raise an exception.
Rule #11: Get the difference - Incoming vs. Outgoing Messages
All commands and queries that are sent from one object to another are also called messages. Like in real life amongst humans we need at least two persons to start a dialogue/conversation. Very often there are many more persons/objects involved. Within VFP's runtime only two objects can communicate at the same a time. The relation established between them can be named "Sender/Receiver", "Guest/ Host" or other pairings like that. One part queries or commands the other. Messages sent are called incoming messages from the receiver's viewpoint, because they are coming from the "outside". The other group of messages called outgoing messages are AKA events. These messages never "leave" the object. They never will be sent to another reference by the object itself. There is no "call out" to a foreign object method, but a call to an own method only. That's why we say those methods are events that get raised! Because outgoing messages never will be sent to another class instance directly, we cannot "sit and wait" until we will be informed / hit by an incoming message. We have to get active ourselves and bind to any outgoing message we're interested in instead.
Rule #12: Get the difference - State vs. Appearance
Both terms, state and appearance, belong to the same part of my taxonomy. Both are meta class attributes describing two static aspects of all class instances at runtime. Let's use a VFP form class as an example. After you created an instance of the form like so:
loForm = CREATEOBJECT("Form") and made it visible
loForm.Show() you can see your new form object showing up in it's post-init appearance. Your form has a lot of properties. All of them, better: the values they have, make up the appearance of the object. BTW: a VFP object doesn't have to stem from a GUI class to have an appearance. Each object's appearance is the entirety of all property values at a given point in the object's lifetime. Moving or resizing our demo form for example will change it's appearance. Thus, our form object have an almost unlimited quantity of different appearances. As we've already learned, each single form property can be valid or invalid. Thus, if all properties are valid, we could state that the object appearance is valid, too. The term appearance relates pure quantity more closely. Therefore, we should use the term state instead, when describing the object quality of being valid or not (a boolean value/quality). From object appearance follows object state, but not exactly vice versa!
BTW: The only object appearance you can describe in your class definition precisely is the one called post-init appearance - if you've done it right and followed my rules so far!
If all of your objects' post-init appearances are completely self-contained, bearing no external references as described in your class definition, and if all of your property values had been explicitly initialized (using code), then you can rerun that portion of code at any time to reset your entire object to it's post-init appearance. Do exactly that before you destroy your class instances and you will never see any dangling references again (at least not your own home-grown)!
Rule #13: Get the difference - Utility vs. Behavior
Both terms, utility and behavior, belong to the same part of my taxonomy. Both are meta class attributes describing two dynamic aspects of all class instances at runtime. Behavior is a collective term for all kinds of object functionality (all kinds of VFP methods). The big difference between behavior and appearance is, that any object that has one or more not so well-behaving methods, must not be "sentenced to death" at once. On the other side: if all procedures work as expected and all functions return correct results, then the entire object's utility is said to be useful. Every single object's behavior is acceptable if it produces what is defined in it's contract (see below). If only one function call returns an error value or one single procedure call raises an exception, then the overall object's utility is set to useless immediately! An object can have many useful and many useless behaviors, the overall utility is a boolean object quality which can only be useful or not!
BTW: If you tend to think about realizing some kind of percentaged object utility, maybe you better redesign your class! Why is it no good idea to create objects with sometimes 70% and maybe sometimes only 30% of it's utility in memory? Well, classes with too much functionality tend to start up with more "broken" behavior due to their complexity than slim/compact ones. Overloading classes with behavior always bears the risk of adding disparate behavior, thus loosing desired tight coupling between data and functionality! Nobody needs the universal Swiss Army Knife class! An all-in-one object suitable for every purpose should be avoided like the plague if we still want to take advantage of the basic OOP principles!
Rule #14: Go for Contracts
Contracting between two objects can be compared to conclude an agreement in the real world. One object acts as the supplier the other is the acceptor/consumer. Okay, the consumer needs a service the supplier offers. Both parties have to agree upon what service should be interchanged and what the terms of contract are. Next, they have to communicate what should be done if one of them breaks the contract. The consumer can only break the contract by passing invalid parameters to the supplier. In that case the contract states, what the supplier has to do in that case (raise an exception or something else?). If the consumer did everything right, the supplier may break the contract because of some internal malfunction. The contract should state in that case what the supplier has to do in such a case (return error value or raise an exception).
Carefully implemented contracting can save you tons of repetitive code! Although VFP misses some native language constructs to simplify contract coding even more, we have enough programming power at our fingertips to implement some full-blown version of our own.
Contracting can be seen as an extended assertion management as well as a generic parameter checking system, and much more (see below).
Rule #15: Separate Interface from Implementation smartly
A big question is: "How to implement a full-blown VFP contracting scheme?" Well, first of all - it is a good idea not to code against implementation but against an interface. But - what can we do within VFP? There is no such thing like an interface definition (like in the .NET languages). Okay, let's grow our own! We need some little tool that helps us setting up all needed interface classes (based on a lightweight VFP base class); we need another tool that may helps us transferring our old parameter checking code from our implementation classes into our new interface classes.
We will end up with our implementation classes now without the overhead of parameter checking code but extended with an additional bridging scheme to link it to the corresponding interface. Creating our own interface classes can help us to achieve a very high level of assertion management. We will be able to reassure that no object can break a contract we've defined inside our interface objects. Later in the run, we can disable the extended contract assertions to get more speed. Finally it could be possible to remove all interface objects and link the implementation objects together directly. A process comparable to deep-freezing the current application development state into an "ultra fast" end user EXE.
BTW: We didn't talk about possible further enhancements! There are other extensions worth looking at. If we choose to create and maintain separate interface classes for each of our reusable classes, then we have room to add more than only one interface class to each of our classes! Different interfaces could expose different sets of commands and queries. We could implement security-relevant interfaces and add them on the fly at runtime subject to the logged-in user.
We could think about a special messaging matrix that only holds our interface objects. The parent-child relations established there would then represent our application's workflow hierarchy and more. I mean, a set of standardized interface objects is easier to handle within an unified messaging concept than a whole bunch of different base classes.
Our implementation objects could also be updated and/or changed transparently in the background during runtime! The flexibility we would gain using such a concept is enormous!
Rule #16: Go for START() and STOP()
No object can do all the work. No reusable class should be that complex! Objects have to be collaborative and communicative. But we've learned that all reusable classes should be designed to create valid objects that are encapsulated, innocently self-contained, not knowing anything about the "world outside" after their INIT() has run.
At this point every object has to be valid (otherwise our factory would have removed it), but in most of the cases these objects aren't of big help - they render useless, because so much external information still is missing. This is the moment to run the object's Start() method.
Starting an object means to bring in all information from the outside needed to make the instance render 100% useful!
Stopping an object means to reset all properties to their post-init state!
The Start() method may be called from an object's INIT() depending on a property called .lAutoStart AS Boolean. The Stop() method is called from a self-defined RELEASE() method or from the DESTROY() method automatically if the object's proper-ty .lUseful is set to TRUE. Both methods are also responsible for any event binding/releasing that should be made.
Rule #17: Get the difference - Start/Stop vs. Init/Destroy
While INIT() and DESTROY() are native VFP event methods, Start() and Stop() aren't, although they could have been!
Within your INIT() code you take care of the objects initial appearance - your INIT() makes the object an exact and therefore valid copy of your class blueprint. Init() is a function. You can return Success or Failure. BTW: within all of my base classes I flagged VFP's INIT() function as PROTECTED!
Within your DESTROY() code you should do any additional housekeeping necessary. BTW: within all of my base classes I flagged VFP's DESTROY() procedure as PROTECTED!
Within your START() code you take care of the objects initial utility - your Start() method takes input parameters that can be used to pass in all external information and references your object needs to collaborate and communicate. After having started your objects property .lUseful should be set to TRUE. Start() is a command, thus never returns any value. Start() also takes care of any kind of native event binding.
Within your STOP() code you reset the object's appearance to exact the same state it has before your Start() ran the first time - including VFP's native event binding.
Rule #18: Get the difference - Parent- vs. Child Objects
What came first the chicken or the egg?
It is always wise to differentiate between a parent object and its children! A VFP form object hosts instances of let's say VFP's TextBox- and CommandButton classes. The form is the parent, the contained controls are the children. These are my rules of thumb you might want to apply in a similar scenario, too:
A child should never know anything about its parent. A parent object should always know all and everything about its children! A child should never send a message to its parent directly using VFP's speciality: parental reference keywords like PARENT, THISFORM or THISFORMSET. Any child should only communicate with the outside either by using references that were passed in during Start() OR by raising events. During startup a parent may send its own reference (or others) to any of it's children's START() methods OR may bind to any of its children's events.
All of my statements apply to reusable, encapsulated classes only! In other words: if you don't intend to create a reusable super-class (let’s say, to include it in your personal framework) then you might code and use everything you like - go and query any parent information like
THIS.WIDTH = THISFORM.WIDTH-10 it is up to you.
BUT: if you code something like THIS.WIDTH = THIS.PARENT.WIDTH in one of your framework's control classes you might get into troubles, because later in the game you may try to put your control some levels deeper into a containment hierarchy. THIS.PARENT references a different object then! Thus, it is a good idea to add your own parent reference property to all of your base classes. Set this reference during Start() and you cannot loose anymore!
Rule #19: Get the difference - OOP vs. PROC
Another fundamental question always is: "Where should I store a needed functionality?" There are two basically different options to choose from. 1st: create a class and implement it as a class feature (OOP). Or 2nd: add it to one of your procedure libraries (PROC). The first "objectifies" your function by adding a new object at runtime, the latter enhances your VFP language through adding another verb, a procedure that can be called when needed. Both approaches have their pros and cons! On the one hand its always faster to call a function directly from a procedure file than to first instantiate an object before one can use its functionality. On the other hand classes have these nice inheritance and polymorphism features and can persist values between calls within their properties.
Many other development languages have no procedures and functions like VFP has but are building their whole universe upon classes. VFP is a typical hybrid language supporting both concepts! Let's have a short look on C# or VB.net where we can find class methods that are marked as STATIC (in VB SHARED). These class methods come close to VFP's procedural concept, because they can be used/called directly without instantiating an object first. Instead of an "ObjectName.MethodName" combination we have to write "ClassName.MethodName" here to get access directly.
Any data that is needed has to be passed into a procedure/function. Thus a function/procedure relates to the verb in a sentence, whereas an object (a class instance) maps to the noun in a sentence more closely. Its up to you to decide what you want to extend: VFP's language with your own verbs, or VFP's objects with your own OOP-ish class extensions. Anyway, there are some rules of thumb we can use to guide our design decisions:
1st: If we do not use any properties of an object, the methods of such a class could also be placed inside a procedure file. If there's no coupling between object's data and functionality AND there is no special need for inheritance because the functionality is very special or finalized, we can drop the class wrapping saving instantiation time!
Have a look at the following function. Would it make sense to you to wrap it into a class just to call it as a method? I don't think so! This function itself is a wrapper to make VFP's SKIP command a little bit more reliable, thus decorating a command/verb. It doesn't make much sense to change "secure skipping" to "the big skipper"!
FUNCTION Skipping(tnSkip AS INTEGER) AS BOOLEAN LOCAL llSuccess ASSERT VARTYPE(m.tnSkip) == "N" ; MESSAGE "wrong parameter type" IF RECCOUNT() > 0 IF m.tnSkip < 0 AND NOT BOF() SKIP m.tnSkip IF BOF() GO TOP ELSE llSuccess = .T. ENDIF ENDIF IF m.tnSkip = 0 IF EOF() GO BOTTOM ELSE IF BOF() GO TOP ELSE GO (RECNO()) ENDIF ENDIF llSuccess = .T. ENDIF IF m.tnSkip > 0 AND NOT EOF() SKIP m.tnSkip IF EOF() GO BOTTOM ELSE llSuccess = .T. ENDIF ENDIF ENDIF RETURN m.llSuccess ENDFUNC
Another example to show what I mean is the following class definition
DEFINE CLASS cTextManager AS Custom FUNCTION MakeNice(tcText AS STRING,; tlOption AS BOOLEAN,; tnOption AS INTEGER) AS STRING *\\ here code to reformat the text RETURN m.tcText ENDFUNC FUNCTION PrintText(tcText AS STRING,; tlOption AS BOOLEAN,; tnOption AS INTEGER) AS BOOLEAN *\\ here code to print the text RETURN .T. ENDFUNC FUNCTION ConvertText(tcText AS STRING,; tlOption AS BOOLEAN,; tnOption AS INTEGER) AS STRING *\\ here code to convert the text RETURN m.tcText ENDFUNC ENDDEFINE
Obviously there are three methods (functions) that neither share nor use any class variables at all. The text to process is always passed in on method invocation together with some (maybe optional) other control values. Okay, we call the class a "Text Manager" but his three functions could be placed inside a simple procedure file as well.
Rule #20: Get the difference - Instance Programming vs. Role Assignments
All my reusable framework classes have a method (a command) called REASSIGN().
... still 2B written...
Rule #21: Go for a good Object Factory
... still 2B written...

2 Comments:
Great article! I'm waiting for the next part. Cheers :-)
Andrzej
Thanx. Still busy adding NEXT topics to this thread. Stay tuned:-)
Post a Comment