Welcome to Foxite.COM Community Weblog Sign in | Join | Help

Passing Multiple Values

One of the questions that I see over and over again on the various technical support forums that I frequent is “What is the best way  to pass multiple values from one object to another”. Let’s begin with the simplest case, where we want to call a procedure (or method) that returns a single value. Obviously can just design the procedure so that it returns the desired result and, inside a form (or other object) method, we call it as a function and capture the returned value to a local variable, like this:

*** Call the external program/procedure and catch the return

luCalcValue = MyProcedure()

This.Control.Value = luCalcValue

Use a Reference

This is fine when all that we want is a single value, but of course, there are many scenarios in which we need to return multiple values from the procedure. Probably the first thing that leaps to mind is to pass a reference to the calling object to the child procedure so that it can access the calling object’s properties and methods directly like this:

luCalcValues = MyProcedure( THISFORM )  or  luCalcValues = MyProcedure( THIS )

While this looks attractive in many ways, it does break encapsulation and is, therefore, not “good OOP practice”. But if “MyProcedure” needs to call a method on a form then this is really the only way to do it. (I have seen it suggested that you explicitly name the instance of the form and then have the procedure look for an object with that name – but this only works if you are using the DO FORM command, and it only works as long as there can only ever be one instance of the form. It is not a good solution!)

However, having an external procedure or object rely on being able to call a method on another object is bad design because the procedure is therefore inextricably coupled to the calling object. It is a fundamental rule of OOP that no object should ever depend upon the internal implementation of another – because such objects cannot be used unless a another object (with the appropriate method) is also involved. If you find yourself in this scenario, then you probably should be thinking about re-designing things anyway.

For example a better approach would be to have the procedure return a flag indicating that the calling object should initiate some appropriate action. That way the decision on what to do in a given situation rests with the calling object, not the child procedure – clearly a much more flexible way of doing things.

Use Public Variables

If we aren’t going to let the procedure manipulate the object, then what about using Public Variables (or declaring Private Variables in the calling method so that they are in scope for the procedure) and having the procedure set their values. But this is really just a non-OOP variation on  passing a reference and it is poor design for all of the same reasons (now the procedure is dependant upon the existence of appropriately named variables which have to be in scope). Worse, if we use Public Variables there is always the risk of “collisions” where the same variable is being changed from more than one place so that the value referred to is not necessarily the one that we were expecting.

Use a Parameter Object

So if we can’t use these methods, what can we do. Fortunately VFP allows us to create and use parameter objects and these really are the best way to go. So the first thing to decide is what should our parameter object be.

If you are using VFP Version 8.0 or later, you can use the “empty” base class (which is also the class used by SCATTER NAME) which can be instantiated and released very quickly because it has no native properties, events or methods. To add properties you have to use the ADDPROPERTY() function.

If you are using an earlier version of VFP then you can use any lightweight base class, providing that it has an AddProperty() method, either ‘relation’, which can only be defined in code, or ‘line’, if you want to define it in a visual class library, will do nicely.

Incidentally this is one of those cases where we really can use a base class directly (other commonly used base classes include SESSION and COLLECTION) – although if you use the same set of values frequently in an application it may be worth creating a standard sub class for the object to avoid the necessity of repeating the code and to handle the maintenance of the properties it uses. So, how do we do this?

Version 8.0 or later

*** Create an instance of the Empty Base class

loParams = CREATEOBJECT( 'Empty' )

*** Add and initialize a string property

llOk = ADDPROPERTY( loParams, 'cName', 'Andy Kramek' )

*** Add and initialize an Array Property

llOk = llOk AND ADDPROPERTY( loParams, 'aList[2,2]', '' )

IF llOK

  ** Populate the Array

  WITH loParams

    .aList[1,1] = 'Akron'

    .aList[1,2] = 'Ohio'

    .aList[2,1] = 'Phoenix'

    .aList[2,2] = 'Arizona'

  ENDWITH

ENDIF

Version 7.0 or earlier

*** Create an instance of a relation

loParams = CREATEOBJECT( 'Relation' )

*** Add and initialize a string property

llOk = loParams.AddProperty( 'cName', 'Andy Kramek' )

*** Add and initialize an Array Property

llOk = llOk AND loParams.AddProperty( 'aList[2,2]', '' )

IF llOK

  ** Populate the Array

  WITH loParams

    .aList[1,1] = 'Akron'

    .aList[1,2] = 'Ohio'

    .aList[2,1] = 'Phoenix'

    .aList[2,2] = 'Arizona'

  ENDWITH

ENDIF

Having got our parameter object, and populated it we can simply pass it around as if it were simply a single value. So now in our procedure, instead of returning a single value, we simply create and return the parameter object:

RETURN loParams

How do we know what we got back?

Now you may be wondering how we know what we got back in our parameter object. Well that is easy too! There are two methods, we can either use the AMEMBERS() function to get a list of all the properties on the parameter object. This is fine when using the EMPTY() base class but can get messy if you are using Relation or Line. Here is an example of this method:

*** Call the procedure that returns the parameter object

loResults = MyProcedure()

*** Use AMEMBERS() to get a list of property names

lnProps = AMEMBERS( laProps, loResults, 0 )

*** Get the Name and the value

FOR lnCnt = 1 TO lnProps

  *** Get the name of the property

  lcName = laProps[lnCnt]

  *** And it’s value 

  luVal = EVAL( “loResults.” + lcName )

  *** Use it however necessary…

  ? lcName, luVal

NEXT

Alternatively we can use PEMSTATUS() to determine if the appropriate property exists on the parameter object (this is easier when you are looking for specific, known values only):

*** Call the procedure that returns the parameter object

loResults = MyProcedure()

*** Check for the property and assign a default value if not found

lcName = IIF(PEMSTATUS( loResults, 'cName', 5 ), loResults.cName, "" )

*** Use the result however necessary…

? lcName

Remember this works both ways…

I have been talking about using a parameter object to get multiple return values, but of course there is no reason why we should not use a parameter object to PASS multiple values as well. There advantages to doing this because it gives us explicitly named parameters. In other words instead of relying on the position of a value in the list of parameters we can simply name. As a result we no longer need to count the parameters either  – think of how many times you have used code like this:

IF PCOUNT() = 3

  *** Passed LastName, FirstName and Initial

ELSE

  IF PCOUNT() = 2

    *** Assume LastName and FirstName

  ELSE

    IF PCOUNT() = 1

      *** Assume LastName only

    ELSE

      *** Nothing passed

    ENDIF && PCOUNT() = 1

  ENDIF && PCOUNT() = 2

ENDIF && PCOUNT() = 3

That now get replaced by:

WITH loParameters

  lcFirstName = IIF(PEMSTATUS( loParameters, 'FirstName', 5 ), loParameters.FirstName, "" )

  lcLastName  = IIF(PEMSTATUS( loParameters, 'LastName', 5 ), loParameters.LastName, "" )

  lcInitials = IIF(PEMSTATUS( loParameters, 'Initials', 5 ), loParameters.Initials, "" )

ENDWITH

This is so much easier to work with and to maintain! If for no other reason than to make your own life simpler I hope you will adopt parameter objects as enthusiastically as I have.

Published Tuesday, July 19, 2005 5:08 PM by andykr
Filed Under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Passing Multiple Values

Tuesday, July 19, 2005 9:07 PM by Andy Kramek
Bob Archer asked me to post his comments; Here they are:

Andy,

I have to disagree with some of your comments in "Pass a reference". You state that doing this causes tight coupling and is a bad OOP practice. Well, yes and no. Tight coupling is bad, passing a reference isn't.

This is one of the big differences I see between a dynamically typed language like VFP and a statically typed language like C#.

A function should accept a certain TYPE (aka class in VFP) not and object. So, programming a function to expect a TYPE certainly does not couple it to another object.

Another OOP concept that VFP doesn't implement here (Whither .Net does) is the idea of an Interface. An interface is a TYPE that you create. Your other types (classes) can implement the interface. If you program to interfaces then setting a function to receive an Interface TYPE and/or return an Interface TYPE is actually good way to go.

An interface is sort of a contract that says, this class has these certain properties and these certain methods. So, my method (function) can rely on the object reference passed to it meeting that contract and/or having that interface. This type checking is enforced by the compiler in C#. In VFP you would have to perhaps do an IsA() parameter type check, perhaps with an Assert().

Interfaces are a widely used way to implement many of the gang of four design patterns with loose coupling.

But, just because VFP doesn't support the creation of interfaces you can still somewhat use this idea although admittedly not as effectively. You don't have interfaces in VFP but you do have Types and inheritance.

Let's say you have a method in VFP that is able to take a reference to a textbox and format the text in that text box to look like a hyperlink. The method will always accept type of textbox. Does this strongly couple the method to an object? No. It just couples it to knowing how to work with an object of TYPE textbox.

I also actually think parameter objects are a lot of extra overhead and code. Also, the bottom line is that after all the parameter object is in essence passing an object that meets a certain contract to a method. Many times I have seen the pattern where the receiving object will have a factory that creates the parameter object (contract) for you. The caller will ask the object for a reference to parameter object. So in the end, aren't you really passing in a known type anyway?

I'm not saying parameter objects aren't useful and helpful at times. Especially when you need objects marshaled across a process boundary of some kind. However, I wouldn't say as an absolute that passing a reference is bad OOP. Try to program to interfaces and you will have much less coupling in general.

Hmmm... as I Google it looks like you did a session on Programming to an interface. ;)

BOb

# re: Passing Multiple Values

Tuesday, July 19, 2005 9:09 PM by Andy Kramek
Hi Bob

I must take issue with one thing you said:

Bob Said: You state that doing this causes tight coupling and is a bad OOP practice. Well, yes and no. Tight coupling is bad, passing a reference isn't.

Andy Sys: I hate to disagree, but I didn't say that at all. What I actually said was:

While this looks attractive in many ways, it does break encapsulation and is, therefore, not “good OOP practice”.

and a little later

However, having an external procedure or object rely on being able to call a method on another object is bad design because the procedure is therefore inextricably coupled to the calling object. It is a fundamental rule of OOP that no object should ever depend upon the internal implementation of another – because such objects cannot be used unless a another object (with the appropriate method) is also involved.

In other words what I am saying is that having the procedure rely on receiving a reference so that IT can call a method on the calling object is bad practice!

I don't disagree with anything else you said here, but then I never said what you read either <bg>

# re: Passing Multiple Values

Wednesday, July 20, 2005 10:16 AM by Boudewijn Lutgerink
Andy,

I agree with most of what you write here. There is one question though.
In the framework I use there is one public object, the application object. Some classes I use, eg behaviorclasses for forms and controls, running in the application, retrieve info from this object and are thus depending on its existance.
Do you consider that bad design? and if so, do you have any recommendations on what I should do?

# re: Passing Multiple Values

Wednesday, July 20, 2005 1:31 PM by Andy Kramek
Re: Boudewijn:

No, it is by no means "bad design" to use a global object to provide information and services to other objects in an application. It only becimes bad design when an object relies upon the specific implementation of that global object.

The key question is if you were to instantiate some other class (say a textbox) as "oApp" would your behavior objects be able to work even though "oApp" was of the incorrect class, or would the behavior object simply crash?

Let me emphasize that I am not saying that the application should work with the wrong application object just that it should not crash! Obviously messaging between obejects is vital in any application, the issue is that the internal implementation (i.e. HOW an object works) should not be reliant on the internal implementation of another.

The presence, or absence, of a particular object and its PEMs are things that can be tested for at run time. It is easy enough to take the appropriate action if the expected item is not there.

However if all of the functionality of an object depends directly on another then I would say you have a problem because if the two objects are really so interdependent then probably they should be a single object.

# re: Passing Multiple Values

Thursday, July 21, 2005 10:28 AM by Boudewijn Lutgerink
Pfff, that's a relief (LOL). So my design was not so bad after all. Thanks for clarifying this.

# re: Passing Multiple Values

Monday, December 05, 2005 9:27 PM by anabisbe@amby.net
Thanks Andy !!

You can find the Spanish version of this article at (Puede encontrar la versión en Español de este artículo en:)

http://www.portalfox.com/modules.php?op=modload&name=Sections&file=index&req=viewarticle&artid=95

Regards / Saludos,

Ana
www.amby.net
www.PortalFox.com

# re: Passing Multiple Values

Friday, December 30, 2005 10:29 PM by S.V.Raman
hi. i am a college student.

i am trying to implement the concept of "passing multiple parameters and retrieving in the report viewer control in vs 2005).

i am unable to write query to find a value in the database by passing multiple parameters.

So kindly help me and send the example coding for me to the given mail address as sooon as possible

bye

# re: Passing Multiple Values

Friday, December 30, 2005 11:13 PM by andykr@tightlinecomputers.com
This is not the right forum for seeking help with specific issues like this. Post your question on one of the free message boards (try www.foxite.com if you don't subscribe to one already). You will get a lot of help very quickly...

What do you think?

(required) 
required 
(required)