While you certainly shouldn't create parameters where you don't need them, I have found that a lack of parameters in a module usually reflects a limited vision of how the module will be used. That limited vision generally translates directly to a limited use of the module.[1]
[1] Very few modules truly have no input or output. Two examples of appropriate parameterless modules are a procedure that simply encapsulates a sequence of statements and a procedure that manipulates global variables. Such programs are the exception rather than the rule.
In many of the cases, modules without parameter lists are fundamentally crippled -- they force a programmer to know about the internals of the module and therefore cannot act as true black boxes. This situation arises most often from an overreliance on global variables.
Consider the example of the parameterless company_name function shown here. This program is driven by an Oracle Forms global variable. It returns the name of the company corresponding to the company ID number in the global.
FUNCTION company_name RETURN VARCHAR2 IS cname company.company_id%TYPE; BEGIN SELECT name INTO cname FROM company WHERE company_id = :GLOBAL.company_id; RETURN cname; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END;
This function works just fine, as long as I make sure that I have set the global variable to the appropriate company ID before I call the function. If I look only at the function's specification:
FUNCTION company_name RETURN VARCHAR2
there is no way for me to know that the function requires a particular global variable. It simply tells me, "I will give you a company name." But for which company? The lack of a parameter renders this function largely unusable.
Fortunately, the situation is easily remedied. The version of company_name below takes a company ID number and returns the corresponding company name:
FUNCTION company_name (company_id_in IN company.company_id%TYPE) RETURN VARCHAR2 IS cname company.company_id%TYPE; BEGIN SELECT name INTO cname FROM company WHERE company_id = company_id_in; RETURN cname; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END;
Now, compare the specifications for these two versions of company_name:
First, a function that hides source of primary key:
FUNCTION company_name RETURN VARCHAR2;
Next, a function that requires input of primary key:
FUNCTION company_name (company_id_in IN company.company_id%TYPE) RETURN VARCHAR2;
In the first case, I don't have any clue whatsoever about how I can provide the company ID to the function in order to look up the name. In order to use this version of company_name, I must either depend on external documentation, which states the reliance on :GLOBAL.company_id, or look at the body of the function. External documentation of modules is always something to be avoided, or at least kept to an absolute minimum. Because it is hard to maintain, it is often out of date. As for taking a peek at the body of the function, well, that simply isn't always possible and is never desirable. You should design your modules so that a developer needs nothing more than the specification in order to understand how to use that module.
In the second case, the specification is very explicit about needing a company ID number in order to look up the name. In the case of company_name, the company ID number is the "linkage" between the code calling company_name and the internal implementation of the function.
Whenever possible, you should make such linkages explicit and visible; the best way to do this is to place those items in the parameter list of the module.
Copyright (c) 2000 O'Reilly & Associates. All rights reserved.