Tips_todo   >   Objectclasses   >   Object Classes
\When I started learning Studio, I disliked object classes and couldn't see how they were supposed to be better than code classes. code classes seemed so much easier to call, create, and use. After a year of working "in the dark using code classes", I attended my first Omnis conference and was "enlightened" by Geir on the beauty of object classes. His line "Anything you can do with a code class, you can do better with an object class" has proved true many times over.
Now object classes are my favourite!
OBJECT CLASS INTERFACE
Omnis has done a fantastic job on the developer interface for object classes.
When you need to use an object class, simply create an object type variable, and set the variable subtype to the object class you need to use. Then Right-click on the variable and select 'Interface manager' from the popup menu. Shazam! Studio opens the 'Interface Manager window' which lists all the methods in the object class along with the parameters. Type a 'Do' statement in your code, then drag and drop a method from the Interface manager ... Studio puts all the parameters in brackets for you!
If you use good consistent naming conventions for your parameters, it will be very obvious to you (and others) what each parameter means. (See Naming Conventions)
You can use the Interface Manager to drag and drop methods from other types of classes, but you can't create a variable and right click on it, the way you can with object classes.
OBJECT CLASS INSTANCES
Object classes can have their own instance variables. This means you can send a bunch of data to an object class at different times, then later tell the object class to process the data. (Just be sure to hold the object class instance open). This gives you the flexibility of breaking a large process into logical steps, rather than having to prepare everything before hand and then sending a whole slug of parameters to a specific method. Also since the object class can store its own instance variable, onces you've told the object something (like a window instance reference) you don't have to keep sending the parameter to the same object class instance.
OBJECT CLASSES CAN EXTEND TASK INSTANCES
Code classes will get you into trouble if you try to create a table class instance inside the code class, you will discover inside the table class that task variables have vanished out of scope.
OBJECT CLASSES CAN HAVE PRIVATE METHODS
If you try to create a complex 'Object' with lots of subroutines using a code class you end up with a lot of visible methods when you want to use the code class. It is confusing trying to find the method you are supposed to call vs. the ones that you shouldn't call. If there is more than one developer working on the project, it is really hard to decide. With object classes you can hide all the subroutines by not prefixing them with $. The method(s) which are to be called from outside are prefixed with $ and will show up in the Interface manager. Much cleaner, much nicer to work with.
Object-oriented code is like a road map. A road map is useful not only for what is shows, but just as much for the detail it DOESN'T show. Imagine trying to use a road map that not only gave you the road, but also listed the topographic, demographic, and annual precipitation information ... not a very useful road map. The same goes for object-oriented Programming ... you just want to show the information that is needed for using the object, not everything inside.
Object classes, they're G-R-E-A-T ! ! !You can call an object class method in a single line, without creating an object type variable.
The syntax for doing this is as follows:
Do $libs.LIBNAME.$objects.OBJECTNAME.$new().$CUSTOMMETHOD('PARAMETERS') Returns VALUE
This single line of code will instantiate the object, call the method, return the value, and then close the object instance. Studio evaluates the first part up to (), then calls the method in the object, sending along the parameters if any.
"Do" and "Calculate" are equivalent, so the following line of code, though different in format is exactly the same as the previous "Do" statement.
Calculate VALUE as $libs.LIBNAME.$objects.OBJECTNAME.$new().$CUSTOMMETHOD('PARAMETERS')
SETTING OBJECT TYPE VARIABLE SUBTYPE ON THE FLY
If you have an object type variable which has the subtype empty (not pointed to an object) you can set the
subtype using the following syntax:
Do $libs.LIBNAME.$objects.OBJECTNAME.$new() Returns oObjectVariable
Once you've set the object type variable's subtype you can use the variable the way you normally would.
Do oObjectVariable.$METHODNAME(pPARAMETERS) Returns VALUE
WHAT'S THE ADVANTAGE OF "OBJECTS ON THE FLY"?
You don't have to use "objects on-the-fly", in fact I hardly used them before rewriting StudioTips. When rewriting StudioTips, I started with the default library name of NEWStudioTIPS. Partly through the rewrite I changed the default library name to TIPS. Guess what happened to all my object type variables ... they were no longer valid.
Using objects on-the-fly, or setting the object type variable subtype on-the-fly, overcomes this problem if you do it right. In StudioTips I'm always in the $clib, so as long as I know the name of the object, setting the object type variable's subtype at the beginning of a method or $construct of a window is easy.
Do $clib.$objects.OBJECTNAME.$new() Returns oObjectVariable
If the object variable is an instance variable, I'll set it in the window's $construct. If it is a local variable I'll set it at the beginning of the method. Immediately after you set the object type variable's subtype you can use the Interface Manager on the variable to look at the object's methods and drag and drop them into your code.
By setting the subtype using notation, if I change the default name of the library, the object type variable continue to point to the correct object class. (So long as the object name doesn't change)
If you are very familiar with the method names of an object, and the object doesn't have a $construct method then might be easier to simply call the object's method on the fly, without an object type variable.
You decide what's best in each situation.A coding good habit (my opinion) is to end your methods with 'Quit method kTrue'. If the method makes it through to the end properly kTrue is returned to the calling method.
If something goes wrong part way through the method or the desired result is not obtained, then
you can exit the method before the end with 'Quit method kFalse'
EXAMPLE:
The calling method uses:
Do $cinst.$METHODNAME Returns FlagOK
If not(FlagOK)
Stop process, OK message if appropriate
Quit method kFalse
End If
Continue process if flag was true
Quit method kTrue
With object-oriented programming you can't be certain what methods might be calling the object, so I find it is best avoid OK messages in the methods. If there was a problem, return kFalse to the calling method and let the calling method decide how best to inform the user or resolve the problem. The habit of return kTrue or kFalse to the calling method is an easy way of communicating to the calling method whether or not execution was successful or failed to get the desired result.
EXAMPLE:
In my base table class superclass I have a method called $getIDRecord(pIDValue) The ID value is sent to the method. The table class method does a $select and $fetch. After the $fetch I tests $rowfetched to see if a record was fetched. If no record was fetched, Quit method kFalse, otherwise Quit method kTrue.
When I call the method:
Do Row.$getIDRecord('IDVALUE') Returns FlagOK
If not(FlagOK)
Notify the user?
Quit method kFalse
End If
Continue process, then...
Quit method kTrue
This sure beats having to test the value of 'Row' everywhere in my code.
NOTE: An exception to the Quit method returns kTrue or kFalse is with "function type" methods and "oContants"This is maybe an obscure situation but having solved the problem once I thought I may has well add it to StudioTips in case another developer had a similar problem.
Let's say you have an Object A, and create an instance of it. The instance could be in a task instance, or a window instance, or even another object instance. Object A's instance has some values in it. For this discussion we'll assume you want to share the Object A's single instance between 2 different windows. These windows might even be part of separate task instances.
Window A opens an instance of Object A. Window A now wants Window B to share that instance of Object A. You don't want to create additional instances of Object A, you want to share the single instance of Object A.
The trick is to set up an item reference variable in Window B and pass a reference of Object A from Window A to Window B. You must include ".$ref" appended to the end of the Object A instance when passing it to Window B.
Click the Run Demo button to try this out.If you want to store a "collection" of object instances in a list, you will hit a bug that exists in Omnis Studio. (Tech Support says it will be fixed in version 4.)
I have wasted MANY hours wondering why I could not get this to work, only to discover it was an Omnis Studio bug. :-( Hopefully this tip, prevents you from doing the same.
What makes this bug tricky to catch, is that is only shows up when you add another line to the list.
Okay, many of you are asking... "What is Doug talking about?". Here's an example: This example uses the "observer design pattern".
Let's say we have a menu called, mSpecial, and we want to allow various objects to register with mSpecial and be notified by mSpecial whenever the user selects a menu item is selected in mSpecial.
We add a public method, $attachObserver(pClassInstanceRef), to mSpecial. Other objects can send an $attachObserver message to mSpecial. If we add an item reference instance variable, iObserverRef, to mSpecial we could set its reference to pClassInstanceRef. The problem is that this would only allow mSpecial to support a single observer. In order to make our code flexible, we decide to add a list variable, "iObserversList", to mSpecial. iObserversList has an item reference column named "InstanceReference". Now we can add as many "observers" as we like by adding a new line to the list each time an $attachObserver message is received.
Each time the user selects a menu item in mSpecial, all the registered "observers" in iObserversList are sent a message notifying them of the menu event that has just occurred.
Everything sounds great up to this point. The trouble is that Omnis Studio (pre v4) has a bug where "object instance references" can't be stored in a list. The references are lost as soon as you add a new line to the list. :-(
STORING OBJECT INSTANCE REFERENCES - BUG WORKAROUND
The following explanation and workaround for this bug has been provided by Tim Stewart.
I guess some memory handling aspect of the underlying Omnis list object does not allocate sufficient discrete resources to separate objects in the list, so they can start conflicting with one another. If you have a list with just one line, it looks as though a contained object retains its integrity, but as soon as you add a second line, item references within the first object become invalid and smartlists get .. dumb! We didn't see the complete loss of table instances Doug mentioned - the overriden $dowork method would still run for example - just ain't much good without any list line stati.
Our workaround for object lists is to store them in a row. We use the column name as the object identifier, so a search works like "Row.Identifier". We haven't noticed any performance differences with the volumes we're currently working with. Initial tests suggest that smartlists retain their status, and the object seems to work. Sending universal messages works with "Row.$cols.$sendall(Row.[$ref.$name].$action)". The obvious limitation is maximum 400 columns,
our current contingency plan will be to use multiple rows with an index list if we approach this number.
Our workaround for item ref lists is to store the text of the $fullname of the reference, having first parsed out "$ivars.", "$tvars." and "$cvars." from the string, and then reevaluate this text into a local item ref where necessary.
Neither is perfect, but they allow us to move forward without a complete redesign of the architecture for this part of our system. We will be cautious about designing anything involving dynamic object collections again though, although this should be a fundamental of OO. I could hear the Java guys chortling as we struggled with this. I am relieved we found a workaround, and glad Omnis are fixing this for version 4.Some programmers use the Quit method [Value] to return the result value to the calling method.
I tend to reserve the return value for kTrue and kFalse, indicating whether or not the method executed correctly
In order reserve the return value for kTrue or kFalse, you need to use 'Field Reference' parameters to 'return' the value
The upside on using Field Reference parameters are:
1. You reserve the return value for kTrue or kFalse
2. You can return more than one result.
The downside on using Field Reference parameters are:
1. You must first calculate the value to a Field, before sending it to the method. (Can't send 'This Value' as a parameter)
2. You can not use Field reference returning methods as 'in-line' methods.
Run the demo for this subject to demonstrate the 2 styles.
The code in the 2 methods is shown below.