| By Hoyt Nelson | Article Rating: |
|
| January 18, 2007 01:45 PM EST | Reads: |
8,244 |
Like Java and C# programmers, PowerBuilder developers can create properties via the undocumented keyword indirect. Properties look like ordinary variables, only their value isn't accessed directly. When you declare a variable using indirect, you have to specify a function that's called when the variable is assigned (a setter) and another function that's called when the variable's value is returned (a getter).
Below is an example syntax for declaring a property using indirect:
// l_patient_id is an instance on a NVO
indirect long l_patient_id { of_set_patient_id( *value ), of_get_patient_id() }
You have to write the getter/setter functions in the same context as the indirect declaration. You cannot use indirect with "pure" global variables; in this article, "global variables" are instance variables declared on a global NVO 'g'. The getter/setter functions would be on that NVO. They can (and should) refer to a private variable:
// Accessed ONLY by the getter/setter functions so it's private
private long l_patient_id
// Getter on the NVO
function long of_get_patient_id()
return l_patient_id
// Setter on the NVO function void of_set_patient_id( long al_patient_id )
l_patient_id = al_patient_id
Given this declaration and getter/setter then assigning to the g.l_patient_id:
g.l_patient_id = 1234
is turned into a function call by the PowerBuilder compiler, in effect:
g.of_set_patient_id( 1234 )
where 'g' is the name of the global NVO. Accessing the value becomes another function call, so:
ll_current_patient = g.l_patient_id
becomes the equivalent of:
ls_current_patient = g.of_get_patient_id()
How Could Properties Be Useful?
You can use properties to make your PB application more robust.
Using properties is a best practice with modern programming languages like Java and C# because they are safer than directly accessed variables. Consensus says that properties make code more robust and reliable, principally because they give the developer greater control of how the data is used.
Properties are more powerful than ordinary variables because the setter/getter functions give you a hook where you can write code, e.g., to validate the data. Is there something you want to do whenever a variable changes? If you don't now you will later, and the setter function gives you a place to write the code to do that.
Example:
Converting Globals to Properties
There are a lot of PB applications out there that were crafted before any of us knew what we were doing. In particular, they make extensive use of global variables, which are inherently unsafe. There's a good reason why we were all taught to avoid globals! The indirect keyword gives us a way to convert globals to properties.
My example comes from a legacy medical application that used a long patient ID instance on the global NVO 'g' to keep track of the current patient. The global patient ID was set and reset a couple hundred times in the application. In some contexts, there was no patient until the user selected a row, or until the code processed a row in a DataWindow. In those circumstances, the global would be set to zero to signify that there was no current patient. When the patient was selected, a new patient ID would be assigned to the global long.
One problem was that another global variable needed to be kept in sync with the global patient ID. Whenever the patient ID changed, the global patient name string had to be reset too. Keeping them in sync was essential. The application perpetually displayed the current patient's name in the MicroHelp area, and it routinely displayed the global patient name on reports, without first refreshing it to make sure that it corresponded to the global patient ID.
This led to bugs. We'd get support issues like "The prescription had the correct patient ID but the wrong name." Inspecting the code, I found that most of the time a change to the global patient ID was done by a call to a function that reset the patient name accordingly. However, there were a dozen patient ID assignments where that function call was missing. Bugs!
There was also nothing in the application that cleared the patient's name from the MicroHelp when the patient ID was zeroed out to signify "no current patient." The MicroHelp conveyed that so-and-so was the current patient, while the application would put up messages like "You must select a patient before..."
I could have simply added a reset-the-patient-name function call to the dozen places where it was missing. However, that would have left the vulnerability intact, and subsequent programmers would have had to be aware of the requirement to call the function whenever they set the patient ID. That wouldn't have done anything to fix the misleading MicroHelp that presented the current patient's name when there was no current patient.
A better solution was to replace the global patient ID and patient name with properties.
Implementing the Global as a Property
The process of converting the variables on the global NVO 'g' to properties took just a few minutes. The process was:
1. Declare private variables on the 'g' NVO to hold the patient ID and patient name:
// On the application object
private long il_patient_id
private string is_patient_name
In effect, these become new global variables equivalent to the old ones that are get/set using the properties.
2. Write getter/setter functions for the new private variables. For the global patient ID:
// Patient ID Getter
function long of_get_patient_id ()
return il_parient_id
// Patient ID Setter
function void of_set_patient_id ( long al_patient_id )
string ls_pt_name // Name shortened for this article! :)
// Validate the patient_id
if IsNull( al_patient_id ) then
g.warn( "NULL patient ID!" )
al_patient_id = 0
elseif al_patient_id < 0 then
g.warn( "Negative patient ID: " + string( al_patient_id ) )
al_patient_id = 0
end if
// Do we HAVE a patient ID?
ls_pt_name = "" // In case the ID is invalid
if al_patient_id > 0 then // Yes
// Does the patient ID exist?
select pt_name into :ls_pt_name from patients where pt_id = :al_patient_id;
if sqlca.sqlcode = 100 then
g.warn( "Invalid patient ID: " + string( al_patient_id ) )
al_patient_id = 0
end if
end if
// Save the patient ID
il_patient_id = al_patient_id
// Reset the patient name property (so the setter() is executed)
g.s_patient_name = is_pt_name // SELECTed above (or blank)
The patient ID setter illustrates the advantage of the property over the global variable. It provides a hook where the patient_id can be validated, especially against the ever-troublesome NULL value. The g.warn() presents its message in a console window during development and always logs the message so the developer will get a clue if some error is leading to assigning invalid patient IDs and the support team can inspect the log file when "wrong name" bugs appear.
Published January 18, 2007 Reads 8,244
Copyright © 2007 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Hoyt Nelson
Hoyt Nelson is an independent contractor, a 10-year PowerBuilder veteran, and father to the amazing Nelson, Kent, Emma, and Molly (Hi, kids!).
![]() |
Arthur Hefti 01/18/07 02:15:38 AM EST | |||
I checked it with PB 11 Beta and it works with .NET generation also. |
||||
- Why SOA Needs Cloud Computing - Part 1
- Cloud Expo and The End of Tech Recession
- The Transition to Cloud Computing: What Does It Mean For You?
- A Rules Engine Built in PowerBuilder
- Sybase Named “Silver Sponsor” of iPhone Developer Summit
- How PowerBuilder Got Its Groove Back
- The Cloud Has Cross-Border Ambitions
- Ulitzer Names The World's 30 Most Influential Virtualization Bloggers
- Ulitzer Named "New Media" Partner of Greatly Anticipated iStrategy Event in Berlin
- Risks and Enterprise Mobility?
- Steps for Success in Enterprise Mobility?
- Are Mobile Luddites Resisting Mobility?
- The Difference Between Web Hosting and Cloud Computing
- Sybase CTO to Speak at 4th International Cloud Computing Expo
- Why SOA Needs Cloud Computing - Part 1
- Cloud Expo and The End of Tech Recession
- The Transition to Cloud Computing: What Does It Mean For You?
- Five Reasons to Choose a Private Cloud
- Seeding The Cloud: The Future of Data Management
- The Threat Behind the Firewall
- Economy Drives Adoption of Virtual Lab Technology
- Tips for Efficient PaaS Application Design
- A Rules Engine Built in PowerBuilder
- Sybase Named “Silver Sponsor” of iPhone Developer Summit
- Where Are RIA Technologies Headed in 2008?
- PowerBuilder History - How Did It Evolve?
- The Top 250 Players in the Cloud Computing Ecosystem
- Custom Common Dialogs Using SetWindowsHookEx
- DDDW Tips and Tricks
- OLE - Extending the Capabilities of PowerBuilder
- DataWindow.NET How To: Data Entry Form
- Book Excerpt: Sybase Adaptive Server Anywhere
- Sybase ASE 12.5 Performance and Tuning
- Working with SOA & Web Services in PowerBuilder
- Office 2003 Toolbar: A New Look For Your Old PowerBuilder App
- Dynamically Creating DataWindow Objects

































