Tips > Objectclasses > Object Classes (All Contents)
When I started learning Omnis 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 create, call, and use. After a year of working without object classes I attended my first Omnis conference and was enlightened by Geir Fjaerli on the beauty of object classes. His words "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 favorite!There are several advantages that object classes have over code classes.
Omnis has done a fantastic job on the developer interface for object classes.
When you need to use an object class.
If you use good consistent naming conventions for your parameters, it will be very obvious to you (and others) what each parameter means. (See
You can use the ) 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. (You can for table classes as of Omnis Studio v4.2)Omnis Studio v4.1 introduced the Object reference variable type. This gives you two options for the variable
: or .When you use
as the variable , Omnis Studio automatically creates an instance of the object class the first time you send a message to the object. The object instance is bound to the variable and is automatically closed when the variable is closed. If a local variable was used for the object instance, the object instance closes when the method is finished. The trouble with type instances is that it is very difficult to pass and store references to them in other instances. What often happens is that you end up with a new instance, rather then a reference to the original instance. To solve this problem Omnis Studio introduce the variable type.When you use $newref to create an instance of the object class.
as the variable , you must useDo $clib.$objects.ObjectClassName.$newref() Returns oObjRef
oObjRef is a pointer to the object instance. The instance is given a life of its own in memory. You can now pass around and make as many copies of oObjRef as you like. Everything points to the original instance of ObjectClassName. Even though oObjRef might be a local variable, the object instance will continue to live on after the method execution is finished. The object instance lives as long as the task which contains it is open. This makes it possible (and easy) to have several windows point to the data in a single object instance. Changing the data in the object instance will be reflected in all the windows which point to that instance. There is a downside to Object reference object instances... you are responsible to delete the object instance if and when you are done with it. You use the $deleteref method to close the instance.
Do oObjRef.$deleteref()
If you forget to delete the object instances that you no longer need, and keep opening new ones, significant memory and resource leaks will occur.
You can get a list of all $listrefs.
instances usingDo $listrefs() Returns List
You can also check if an $validref.
is valid usingIf oObjRef.$validref()
You can copy a $copyref
object instance usingCalculate oObjRef2 as oObjRef.$copyref()
David Swain has written some excellent Omnis Technical Newsletters on Object Classes and Object vs. Object Reference variable types. www.omnis.net/technews/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.ClassName.$new().$MethodName(Parameters) Returns Value
This single line of code will instantiate the object, call the method, return the value, and then close the object instance. Omnis 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 Do $libs.LibName.$objects.ClassName.$new().$MethodName(Parameters)
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.ClassName.$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(Parameters) Returns Value
You don't have to use objects on-the-fly. I use them in situations where the default object class might be substituted with a different object class. For example I may switch the object class I'm using inside a table class depending on the data base the application is currently connected to, or in a multi-library application I might look for an object class in the $clib and point to it instead of the default object class in a base classes library.If you want to share an object instance with another class instance of any type, use the $newref.
variable and instantiate the object instance usingSee the topic
Instead of setting item references to the object instance, you can receive and copy the Object Reference. All the copies of the point to the same object instance.There are many differing opinions on the best place to store your own global variables. I have tried a few different things. I don't have the perfect solution, but I'm pretty happy with it.
I regard constants as memory-only type data which gets set up on opening the library, or at the moment a method requests the value of the constant. Therefore, I don't want to store my constants in the 'data file'.
When I first started using Omnis Studio, I put my constants in the Startup_Task task variables. This worked until my task variables pane started to get cluttered with all kinds of constants, many of which I only needed occassionally.
I tried using a
as a memory only file mode. Using a file class works pretty nice with the . You can view, and drag and drop the constants directly into your code. However, I use multiple libraries for my application and any constants in my sub-libraries would get scary to read when I closed the other library which had the memory-only file format. They change to the file format mapping number right before your eyes making the code a bit hard to read.I tried moving my constants from the Startup_Task task variables to the Startup_Task instance variables. This worked good for decluttering my task variables pane, but I could no longer view the constant variable names from other classes in my application. A spelling error meant the code would not work, and if I forgot the exact spelling, I'd have to go to the Startup_Task, look inside, click the instance variables pane, then go back to my code. Not very convenient.
The solution I finally settled on was to create an oConstants object class which gets instantiated by the Startup_Task task variable cn.
I then add a property method for each constant value which I need for my application.
Each of these property methods returns the specified constants value.
If I want to be able to set any of the properties I add a $:AppName.$assign(pValue) setter method.
I then add an object type task variable, cn, to the Startup_Task and point it to oConstants.
Now in the method editor from any one of my classes I can easily access any of my custom constants. The Omnis Studio notation helper assists me as I type the code.
Calculate AppName as cn.$:AppName
or
Calculate Mssg as con("Please contact the developer, ",cn.$:DeveloperName,".")
Click the button in the window to view a sample oConstants object class.Setting up an oConstants object class for your application's custom constants if easy to do.
; $:AppName (method)
; Hard coded application name.
Quit method 'StudioTips'
; $:AppName.$assign (method)
; Copy the value to an ivar.
Calculate iAppName as pValue
Quit method kTrue
; $:AppName (modified method)
; Return the ivar value.
Quit method iAppName
Now in the method editor from any one of your classes you can easily access any of your custom constants. The Omnis Studio notation helper assists as you type the code. You can use your custom constants for in-line calculations.
Calculate Mssg as con("The application ",cn.$:AppName," has been updated.")
Click the button in the window to view a sample oConstants object class.There are a number of techniques you can use to save and restore your custom constants. I like to use a row variable for all of the custom constants and then save the row in the database. That way, if I add a new custom constant I don't have to modify the table in the database.
Here's the steps for implementing the technique which I use:
; $initialize (method)
; Define the constants row variable used in this object.
Do iRow.$definefromsqlclass('sConstants_listdef')
If iRow.$colcount=0
; Log an error.
; Enter code here to suit how you are handling errors.
Calculate FlagOK as kFalse
Else
; Load the constants row from the database or some other source.
; Enter code here to load iRow from where ever the data is stored.
Calculate FlagOK as kTrue
End If
Quit method FlagOK
You can automatically add property methods to your oConstants object using notation.
The technique I use is to loop through the columns of the row variable defined by sConstants_listdef in the $initialize method lof oConstants and check for the appropriate getter property methods in the oConstants object class. If a getter method is missing, I add the getter and setter methods using notation.
; $init_syncPropertyMethods (method called by $initialize)
; Preset the flag to true.
Calculate FlagOK as kTrue
Set reference rClass to $cinst().$class
; Loop through the iRow columns and check for a setter property method for each column.
Do iRow.$cols.$first Returns rCol
While rCol
; Does a getter method exist.
Calculate MethodName as con('$:',rCol().$name)
If not($cinst.[MethodName].$cando)
; The method does not exist. Add it.
Do rClass.$methods.$add(MethodName) Returns rMethod
If rMethod
; Set the method text.
Calculate Text as con("Quit method iRow.",rCol().$name)
Do rMethod.$methodtext.$assign(Text)
; Does a setter method exist.
Calculate MethodName as con('$:',rCol().$name,'.$assign')
If not($cinst.[MethodName].$cando)
; The method does not exist. Add it.
Do rClass.$methods.$add(MethodName) Returns rMethod
If rMethod
; Add a field reference parameter. (Last parameter 'bIsParameter')
Do rMethod.$lvardefs.$add('pfValue',kFieldreference,,,kTrue) Returns rVar
If rVar
; Set the method text.
Calculate Text as con("Calculate iRow.",rCol().$name," as pfValue")
Calculate Text as con(Text,kCr,"Quit method kTrue")
Do rMethod.$methodtext.$assign(Text)
End If
End If
End If
End If
End If
; Next column
Do iRow.$cols.$next(rCol) Returns rCol
End While
Quit method FlagOK
When you get fairly deep into the object-oriented mindset, you start to see that everything in Omnis Studio is an object. Objects have attributes, also know as properties, plus methods that do things. Omnis Studio classes have built in properties and methods which you can view in the
after you select the class in the .Let's say we create an object class called oApplicationProperties.
Now we decide to add some properities to this object. A public method is added to oApplicationProperties for each property. e.g.
$:AppName, $:DeveloperName, $:VersionNum/code>)
The code behind each of these property methods ends with Quit method RetValue
.
You can make a property assignable, by creating a $assign method for the property.
e.g. $:AppName.$assign(pAppName)
The public method name actually includes the .$assign suffix in the method name!
You can now instantiate oApplicationProperties using an instance variable in a window class, and then use the instance variable name followed by the public method name as the $dataname for the entry fields in the window class.
e.g. Instead of iList.ColName, you would use ioProperties.$:AppName
If there is a .$assign suffixed method available for a property, Omnis Studio will automatically call the $assign method in the data object just prior to the evAfter event for the entry field.
Click the button in the window to open a Data Object demo window.
Data Object Advantages
There are several advantages for using data objects:
- You can use data objects to create a layer between your interface classes (window and report classes) and your persistence classes (table classes/database). This layer can give you finer control and data validation between these classes and the flexibility to use different field names.
- You can encapsulate all the functionality of a complex set of data into a single object with a clearly defined public interface.
- The $assign suffixed method is called before evAfter allowing you to execute code prior to the evAfter event. There may be situations where this would be useful.
Data Object Problems
There are some drawbacks to using data objects.
- A list property cannot be directly used in a window object $dataname.
If I have a data object property which is a list, you can't put that property name directly into a window list object. The work around is to create a list variable in the window class and Calculate iListVar as ioDataObject.$:TheList
- Data objects can not be used in web client remote forms. To the best of my knowledge remote forms will only support row and list variables.
- Creating the data objects can be time consuming. You have to create a public method for each property, and a .$assign suffix method for each assignable property.
Data Object ivars
You can directly access the ivars in your data object. This is demonstrated in the demo included in StudioTips with the tip.
Instead of setting the $dataname property of a field to ioDataObject.$PropertyMethodName, you set it to ioDataObject.iVarName.
The advantages of doing this are:
- You don't need to create a property method and $assign method for each property.
- You can display list and row variables directly in list field objects.
The disadvantages of doing this are:
- You can't intercept getting and setting the data object's properties.
- Changing the name of an ivar in your data object will break any GUI objects that were using the ivar name.
Object-oriented purists would argue against directly accessing a data object's variables... and they do so for valid reasons, if you only access and manipulate an object's properties through its interface (public methods) your code will be stronger.