Consider this Oracle Forms function, which retrieves the name of a company from its primary key: FUNCTION company_name company_id_in IN company.company_id %TYPE RETURN VARCHAR2 IS CURS
Trang 1assignment operator itself already serves as the "action" for that statement
I can make a few, minor adjustments in these module names and end up with much more readable code, as shown in this example:
Trang 2net_profit_for_single_company
Don't forget that you or someone else will have to type those program names!
22.1.2 Develop Consistent Naming Conventions for Your Formal
Parameters
A parameter plays a specific role within a program; its name should indicate this difference I
recommend that in order to further distinguish parameters from other PL/SQL identifiers you include the parameter mode right in the name of the formal parameter
A parameter has one of three modes: IN, OUT, or IN OUT An IN parameter passes a value into the module, but its value cannot be modified An OUT parameter passes a value out of the module, but its value cannot be referenced in the module An IN OUT parameter can be both referenced and modified in the module
By incorporating the parameter mode directly into the parameter name, its purpose in the module is self-documenting Whenever you encounter that parameter in the code, you know that you are
looking at a parameter, not a local variable, and you know exactly the ways in which that parameter can be used and/or changed
In order for the parameter mode to stand out in the formal parameter name, you can include it either
as a suffix or as a prefix As a suffix, the mode is appended to the end of the parameter name, as follows:
PROCEDURE combine_and_format_names
(first_name_inout IN OUT VARCHAR2,
last_name_inout IN OUT VARCHAR2,
full_name_out OUT VARCHAR2,
name_format_in IN VARCHAR2 := 'LAST,FIRST');
The prefix standard would result in a parameter list for the above procedure as follows:
PROCEDURE combine_and_format_names
(inout_first_name IN OUT VARCHAR2,
inout_last_name IN OUT VARCHAR2,
out_full_name OUT VARCHAR2,
in_name_format IN VARCHAR2 := 'LAST,FIRST') ;
You could also opt for a minimum of typing by simply using the first letter of each parameter mode:
PROCEDURE combine_and_format_names
(io_first_name IN OUT VARCHAR2,
Trang 3io_last_name IN OUT VARCHAR2,
o_full_name OUT VARCHAR2,
i_name_format IN VARCHAR2 := 'LAST,FIRST') ;
In any of these cases, a quick glance over the code identifies which objects represent parameters in the program You can also more easily catch logic errors, such as the following case of an illegal update of an IN parameter:
in_name_format := 'FIRST MIDDLE LAST';
A parameter with an "in" suffix or prefix should never have its value changed A parameter with an
"out" suffix or prefix should never be used on the right-hand side of an assignment because its value
is indeterminate in the program (until it is given a value)
22.1.3 Name Packages and Their Elements to Reflect the Packaged
Structure
Given that a package provides a new layer or context over the normal variables and modules, you should take some special care in naming your packages and the elements within them The name you use for a standalone module, in particular, will change when you move it inside a package Consider the following example If I develop a set of standalone procedures and functions to maintain lists of information, I might name them as follows:
PROCEDURE list_create (list_in IN VARCHAR2);
(list_in IN VARCHAR2, position_in IN NUMBER,
item_out OUT VARCHAR2);
END list;
At first glance this looks reasonable enough The real test, however, comes when you try to use the modules Let's try it To create a list using the package procedure, I would execute a statement like
Trang 4this:
list_pkg.list_create ('company_names');
Now, that looks silly The "list" is mentioned in both the package name and the module name It didn't make much sense to carry over the standalone module names to the package names A much cleaner naming scheme should produce executable statements that remove the redundancies:
PACKAGE list_pkg
IS
PROCEDURE create (list_in IN VARCHAR2);
PROCEDURE get
(list_in IN VARCHAR2, position_in IN NUMBER,
item_out OUT VARCHAR2);
(list_in IN VARCHAR2, position_in IN NUMBER,
item_out OUT VARCHAR2);
indicator) reduces the readability of the code
Trang 5Previous: VI Making PL/
SQL Programs Work
Oracle PL/SQL Programming, 2nd Edition
Next: 22.2 Build the Most Functional Functions
Trang 6Previous: 22.1 Select
Meaningful Module and
Parameter Names
Chapter 22Code Design Tips
Next: 22.3 Take Full Advantage of Local Modularization
22.2 Build the Most Functional Functions
I like functions They can replace an awful lot of complex logic in an expression with a simple
statement of the result of all that logic The following tips will help you construct functions that are as useful as possible
22.2.1 Avoid Side Effects in Functions
Your function has one purpose in life: returning a single value via the RETURN statement If it does anything else, such as update global or database information, then that function has potentially
created a side effect Side effects generally limit functions' usefulness Let's look at a few examples and discuss how you can avoid side effects
22.2.1.1 Do not use OUT and IN OUT parameters
Although a function returns its value with the RETURN statement, PL/SQL allows you to define OUT and IN OUT parameters for a function If you do that, the function can actually pass changed data back through the parameter itself into the calling block of code through the parameters This is generally not a good idea, and is a typical side effect in a function
The format_name function below contains side effect parameters Let's examine the impact of these parameters The function takes a first name and a last name and returns a full name with the format LAST, FIRST Because a requirement of the application happens to be that all names must be in uppercase, the function also converts the first and last names to their uppercase versions It uses two
IN OUT parameters to do this
FUNCTION format_name
(first_name_inout IN OUT VARCHAR2,
last_name_inout IN OUT VARCHAR2)
Trang 7last_name_inout := UPPER (last_name_inout);
RETURN last_name_inout || ', ' || first_name_inout;
END;
If the application requires uppercase names, you may wonder, then, what is wrong with converting the first and last names to uppercase in format_name? First of all, this approach may be trying to meet the requirement a bit too enthusiastically If the formatted name the output, that is, of the function must be in uppercase, you can certainly accomplish that without modifying the
22.2.1.2 Switch to a procedure with IN OUT parameters
If you do want a module that will convert both individual name components and the combined name
to uppercase, then you would be better served with a procedure:
PROCEDURE combine_and_format_names
(first_name_inout IN OUT VARCHAR2,
last_name_inout IN OUT VARCHAR2,
full_name_out OUT VARCHAR2)
IS
BEGIN
first_name_inout := UPPER (first_name_inout);
last_name_inout := UPPER (last_name_inout);
Trang 8You avoid providing a function with side effects (i.e., other actions and impact beyond the return of a value) Let's take a look at the way these different modules would appear in your code
● Let's first look at the function version All our attention is focused on depositing the formatted name into the caller_name item:
full_name := format_name (first_name, :last_name);
● Now let's look at the procedure version The use of the plural of "name" in the name of the procedure indicates a change to more than one of the parameters:
combine_and_format_names (first_name, last_name, full_name);
Generally speaking, when your function has a side effect such as the uppercasing of the parameter values, that function is less broadly useful What if programmers don't want to have the other values uppercased? They would have to write their own function that does the same thing as format_name, but without the UPPER statements This code redundancy will create nightmares down the road If the function is supposed to return a formatted name based on the first and last names, then that is all it should do never use OUT and IN OUT parameters with a function
22.2.1.3 Don't interact with users
Such parameters are not the only kinds of side effects you may be tempted to slip into a function Consider this Oracle Forms function, which retrieves the name of a company from its primary key:
FUNCTION company_name (company_id_in IN company.company_id
%TYPE)
RETURN VARCHAR2
IS
CURSOR name_cur IS
SELECT name FROM company
WHERE company_id = company_id_in;
Trang 9ACKNOWLEDGE);
RAISE FORM_TRIGGER_FAILURE;
END IF;
END;
When the company ID returns an existing company, the function properly closes the cursor and
returns the company's name When the SELECT statement comes up empty, however, I change my approach completely Rather than return a value, I go into the equivalent of PL/SQL panic mode: ring the bell, display a message (and force the user to acknowledge that message), and then fail out of the calling trigger I don't even close the cursor! Rather strong stuff, and very much out of place in this function
My response to invalid data is a side effect in the function, made all the worse by the fact that my side effect completely substitutes for a RETURN When the company ID does not find a match, I do not execute a RETURN statement at all
The consequences of my error handling in this function are:
● This function can be called only in Oracle Forms It is, however, a fairly generic task: take a primary key and return a name/description It could be a function stored in the database and available to any program that needs to perform the lookup I have now made that impossible
● Anyone who calls this function gives up control over his or her own program You cannot decide for yourself what to do if the name is not found That message is displayed whether or not it is appropriate That bell sounds even if users don't like to announce to everyone around them that they made a mistake The best you can do is code an exception handler for
FORM_TRIGGER_FAILURE in whatever trigger or program calls company_name
A much better approach to the company_name function is simply to return a NULL value if the
company is not found Since the company name is a NOT NULL column, a NULL return value
clearly indicates "no data found." This new approach is shown in the following example:
FUNCTION company_name (company_id_in IN company.company_id
%TYPE)
RETURN VARCHAR2
IS
CURSOR name_cur IS
SELECT name FROM company
WHERE company_id = company_id_in;
Trang 10on an appropriate consequence, which might well consist of every action formerly embedded in the function itself:
new_company := company_name (:company.company_id);
At least now the programmer has freedom of choice
Side effects in functions come in many shapes and flavors If you remember to keep the focus of the function on computing and returning its value, the resulting module will be more effectively and widely used
22.2.2 Use a Single RETURN Statement for Successful Termination
A function exists to return a single value The best way to structurally emphasize this single-minded focus in the body of the function is to make sure that the only RETURN statement in the execution section of the function is the last executable statement Consider the following code-translating function:
FUNCTION status_desc (status_cd_in IN VARCHAR2) RETURN
VARCHAR2
Trang 11BEGIN
IF status_cd_in = 'C' THEN RETURN 'CLOSED';
ELSIF status_cd_in = 'O' THEN RETURN 'OPEN';
ELSIF status_cd_in = 'A' THEN RETURN 'ACTIVE';
ELSIF status_cd_in = 'I' THEN RETURN 'INACTIVE';
END IF;
END;
Each of the different RETURN statements is actually a different exit point in this function You are now probably saying to yourself, "This is such a simple program What's wrong with using the
different RETURN statements?"
Granted, the program is straightforward and short enough so that, at a glance, you understand its purpose and follow the exit flow Unfortunately, it also is prone to failure What if the status code that
is passed into status_desc is not one of C, O, A, or I? Then the function does not even execute a
RETURN statement, leaving the calling program with indeterminate results Now examine the
version of status_desc that relies on a single RETURN:
FUNCTION status_desc (status_cd_in IN VARCHAR2) RETURN
VARCHAR2
IS
return_value VARCHAR2 (20) := NULL;
BEGIN
IF status_cd_in = 'C' THEN return_value := 'CLOSED';
ELSIF status_cd_in = 'O' THEN return_value := 'OPEN';
ELSIF status_cd_in = 'A' THEN return_value := 'ACTIVE';
ELSIF status_cd_in = 'I' THEN return_value :=
'INACTIVE';
END IF;
RETURN return_value;
END;
Here, my IF statement assigns the description to a local variable After the IF statement, the
RETURN statement serves its purpose in life, which is to pass the value of the variable back to the calling program Because I initialize the temporary description to NULL, if the status code is not one
of the chosen four, then I return NULL In any case, the result is that, by placing the RETURN
statement at the last line of the execution section of the function, I always execute a RETURN
statement when the function completes successfully There are no holes in my return logic
This approach is absolutely critical when your function is more elaborate and longer than 10 or 20 lines in length, perhaps even hundreds of lines long It can be very difficult to follow multi-page complex logic, and nearly impossible to confirm, simply by reading the code, that the function
always issues a RETURN statement for all branches of the logic Your application works much more reliably if you build right into it the requirement that the only RETURN statement in the function
Trang 12occurs at the end of the program
22.2.2.1 Build a function template
I realize that it is one thing to read through all of the tips in this book and, as you read them, nod in agreement with their wisdom It is quite another thing to both remember the tips and make the time to put them to use in your code The best way I have found to at least improve the chances that I follow even my own advice is to set hard and fast rules and then stick to them When it comes to "Use a single RETURN statement for successful termination," I try to always follow two simple rules when creating a function:
● Always declare a local variable named "return_value" with a datatype that matches the
RETURN clause of the function If I am returning a VARCHAR2, declare a VARCHAR2 local variable If I am returning data with the same datatype as a table column, declare that Here are two examples:
FUNCTION net_sales (company_id_in IN company
company_id%TYPE) RETURN NUMBERIS
return_value NUMBER;
BEGIN
END;
or:
FUNCTION company_name (company_id_in IN company
company_id%TYPE) RETURN company.name%TYPEIS
return_value company.name%TYPE;
BEGIN
END;
● Always make the last executable statement of my function the RETURN statement as follows:
RETURN return_value;
By following these two rules, the general structure of my function now looks like this:
FUNCTION function_name (parameter_list) RETURN datatype
Trang 13RETURN return_value;
EXCEPTION
optional exception section
END;
The name of the local variable states quite clearly that it is standing in for the data that is to be
returned by the function Any time this return_value variable is assigned a value in the function, you know that the function is fulfilling its purpose of generating a value to be returned with the RETURN statement It provides a clean, understandable, and predictable structure for the function I have even gone so far as to create a template script on disk that implements this guideline It looks like this:
/* Filename on companion disk: functmpl.sf */
CREATE OR REPLACE FUNCTION fname () RETURN datatype
One exception to the rule of a single RETURN statement in a function concerns exception handling
A function should return a value, whether it completes successfully or it fails Upon successful
completion, you can use the above structure to guarantee that the RETURN executes What if the function fails? Any exception handler in a function should have as its last executable statement a RETURN of its own, as this example shows:
Trang 14problem Do not process the error inline PL/SQL provides a very structured distinction between the executable body of the program and the exception section As soon as you hit an error or logical exception to normal processing, RAISE an exception to transfer control out of normal execution
22.2.3 Avoid Exception Handlers for Normal Program Exits
There is always only one entry point for a module: the first executable statement is always the one that follows the BEGIN statement PL/SQL does not allow you to enter a block at an arbitrary line of code The same cannot be said, however, for the way in which a module terminates A PL/SQL
program unit may complete its execution either through the last executable statement, a RETURN statement, or the exception handler section, as is appropriate:
● The last executable statement in a program is the normal exit point for a procedure
● The RETURN statement is the normal exit point for a function
● The exception section is the way out of a module that has hit an error and raised an exception
A programming language like PL/SQL is constructed very carefully: every keyword and element of syntax is chosen with a specific purpose Although you can at times justify using a language construct
in an unorthodox way, in most cases such an action will raise a red flag and be closely examined One example of such linguistic abuse occurs when a programmer uses an exception handler to perform a normal program exit Here is an example:
FUNCTION company_name (company_id_in IN company.company_id
SELECT name INTO cname FROM company
WHERE company_id = company_id_in;
RAISE found_it;
Trang 15The found_it exception is not an exception at all It indicates successful completion of the program Rather than use an exception, the function should simply issue a RETURN with cname, indicating that the SELECT was successful:
FUNCTION company_name (company_id_in IN company.company_id
SELECT name INTO cname FROM company
WHERE company_id = company_id_in;
Programmers are often tempted to use the RAISE statement because it acts like a GOTO: it
immediately halts execution of the program and transfers control to the exception section This GOTO-like behavior can be very convenient when a programmer cannot figure out how to use IF statements in a structured way to neatly end the program In this case, the RAISE statement becomes
an easy way out
If you find yourself writing programs that rely on the RAISE statement to do something besides handle exceptions, take a step back Examine the logical flow of the program and explore how you can use the standard control structures (IF, LOOP, and perhaps even GOTO as a last resort) to
accomplish your task instead
Trang 1622.2.4 Use Assertion Modules to Validate Parameters and Assumptions
Just about every piece of software you write makes assumptions about the data it manipulates For example, parameters may have only certain values or be within a certain range, or a string value should have a certain format In both of these cases, an underlying data structure is assumed to have been created It's fine to have such rules and assumptions, but it is also very important to verify or
"assert" that none of the rules is being violated
One approach you can take in this verification process is to make use of the IF statement at the
beginning of your program The status_desc function shown below uses the conditional construct to implicitly validate the value of the argument The function does not actively or explicitly check to see
if the status code argument is one of C, O, A, or I Instead, the IF statement returns a non-null value only if a valid code is provided
FUNCTION status_desc (status_cd_in IN VARCHAR2) RETURN
VARCHAR2
IS
return_value VARCHAR2 (20) := NULL;
BEGIN
IF status_cd_in = 'C' THEN return_value := 'CLOSED';
ELSIF status_cd_in = 'O' THEN return_value := 'OPEN';
ELSIF status_cd_in = 'A' THEN return_value := 'ACTIVE';
ELSIF status_cd_in = 'I' THEN return_value :=
On the other hand, what if a legal value should always be passed to status_desc? What if a different value means that a programmer has coded her use of the function improperly? If status_desc is an
"internal" function accessed only by programmers and not intended to handle user input, then an illegal status code probably reflects a typographical error or a misunderstanding of the program's use
In this case, the lack of feedback by status_desc on the invalid argument results in hard-to-trace
errors Because the programmer is not informed of the typographical mistake when it happens, the problem propagates through other modules before it surfaces
22.2.4.1 Trap invalid argument values
If the status_desc function is built to accept only four valid entries, then it should raise a red flag when that rule is broken The following function shows a version of status_desc that adds an ELSE
Trang 17clause to trap an invalid entry:
FUNCTION status_desc (status_cd_in IN VARCHAR2) RETURN
VARCHAR2
IS
return_value VARCHAR2 (20) := NULL;
BEGIN
IF status_cd_in = 'C' THEN return_value := 'CLOSED';
ELSIF status_cd_in = 'O' THEN return_value := 'OPEN';
ELSIF status_cd_in = 'A' THEN return_value := 'ACTIVE';
ELSIF status_cd_in = 'I' THEN return_value :=
With this new version, programmers get immediate feedback if they pass an illegal value to
status_desc That problem is solved, but I personally find this solution less than totally satisfactory Since it forces me to add several lines of code to the body of my program lines that were added simply to handle values that never should have gotten into the program this approach is somewhat intrusive and inelegant Consider the alternative below:
FUNCTION status_desc (status_cd_in IN VARCHAR2) RETURN
VARCHAR2
IS
return_value VARCHAR2 (20) := NULL;
BEGIN
/* Assert that the status code is valid */
assert_condition (status_cd_in IN ('C', 'O', 'A',
'I'));
/* Now perform processing of valid argument */
IF status_cd_in = 'C' THEN return_value := 'CLOSED';
ELSIF status_cd_in = 'O' THEN return_value := 'OPEN';
ELSIF status_cd_in = 'A' THEN return_value := 'ACTIVE';
ELSIF status_cd_in = 'I' THEN return_value :=
'INACTIVE';
END IF;
RETURN return_value;
END;
Trang 18The procedure, assert_condition, looks like this:
PROCEDURE assert_condition (condition_in IN BOOLEAN)
status_cd_in IN ('C', 'O', 'A', 'I')
This kind of a program is called an assertion module because its single Boolean parameter asserts
that a condition is true, and leaves it to the module to reject that claim Clearly, there isn't much to assert_condition It just hides an IF statement behind a procedural interface Yet, by including the call
to assert_condition, I can let the remainder of the body of my program concentrate on handling the valid data properly I do not have to include special handling for illegal values
Because assert_condition is very generic, I can use it in any program that has a condition I can
express in a single Boolean expression I don't have to code special nested logic repeatedly I tell assert_condition what rule I want to test, and leave it to the module to take action (raise an exception) when necessary
22.2.4.2 An application-specific assertion module
Of course, you can build less generic versions of assert_condition The next function shows an
assertion module for Oracle Forms that displays an optional message to the screen:
Trang 19/* Raise an exception specific to Oracle Forms */
RAISE FORM_TRIGGER_FAILURE;
END IF;
END;
You can even produce assertion modules that handle very specific kinds of conditions If you do a lot
of work with record groups in Oracle Forms, you find yourself checking repeatedly at the beginning
of your program to see that the name or handle for the record group points to a valid record group You do this with the ID_NULL and FIND_GROUP built-ins, as follows:
IF ID_NULL (FIND_GROUP ('monthly_sales'))
Then simply call that assertion program:
PROCEDURE assert_valid_group (rg_name_in IN VARCHAR2)
/*
|| Assertion: if there is not a valid Id for the record
|| group, display message and raise exception This is a
|| "design" error; code should not continue executing
Trang 2022.2.4.3 Is an assertion module a side effect?
But wait If I call assert_condition from within a function and the assertion fails, the function will not execute a RETURN statement Doesn't the use of an assertion module violate my guideline to always return a value from a function? The assert_condition procedure bails out of the calling module (or at least forces a transfer to the exception section) without any regard for whether a RETURN statement
is needed Even worse, doesn't the Oracle Forms version containing a MESSAGE statement
contradict my suggestion to avoid side effects in a function?
Yes and no Yes, I admit that when assert_condition RAISEs its exception, you do not issue a
RETURN statement (unless you code for it yourself in the function, but then you would have to know which exception was RAISEd by assert_condition) I also admit that the MESSAGE is a side effect in the function, the main purpose of which is to return a row number in a table
But I can explain everything, honest!
You see, the point of assert_condition is to test the underlying assumptions in the very structure of the module If status_desc is called with an invalid code, that kind of error is very different from, say,
a NO_DATA_FOUND when obtaining the name for a particular company ID number The
status_desc function as a whole is invalid if it is not passed a valid code In this context, it doesn't even make sense to continue processing The assert_condition procedure uncovers design level errors
in the code that must be corrected before you can even worry about data entry errors or other
application-level concerns
The logic behind assert_condition is similar to the way Oracle Forms handles level 25 errors when it executes a form No matter how high a value you have set for SYSTEM.MESSAGE_LEVEL, Oracle Forms always halts processing in a form when it encounters a level 25 error, which indicates a design error in the application
Previous: 22.1 Select
Meaningful Module and
Parameter Names
Oracle PL/SQL Programming, 2nd Edition
Next: 22.3 Take Full Advantage of Local Modularization
Trang 2122.1 Select Meaningful
Module and Parameter Names
Book Index 22.3 Take Full Advantage of
Trang 22Previous: 22.2 Build the
Most Functional Functions
Chapter 22Code Design Tips
Next: 22.4 Be Wary of Modules Without Any Parameters
22.3 Take Full Advantage of Local Modularization
A local module is a procedure or function that is defined within the declaration section of another module The scope of a local module is the module in which it is declared It is invisible to all other modules, and can be called only from within that defining module
Few PL/SQL developers are aware of the local module feature, and fewer yet take full advantage of this capability Yet I can think of few other aspects of the language that are more important to
constructing clean, elegant, easily maintained programs I strongly encourage you to use local
modules at every possible opportunity, and offer several examples in this section to highlight their usefulness
Local modules are very handy ways to "normalize" redundant code inside a program If you perform the same calculation over and over again in a module, don't hardcode the calculation repeatedly Instead, place it in its own function or procedure and then call that module The power of such an approach is clear when you examine the following program In the format_data procedure, I extract a value from the rg_sales record group, divide it by projected sales, and convert to a formatted string over and over again This code is taken from a production Oracle Forms application, and is actually just a small portion of the full set of more than two dozen repetitive calculations
Trang 23Recorder or some other utility to automate the process However you manage it, though, you still end
up with lots of repetitions of the same fragment of code You still end up with code that is hard to maintain
Because I have exposed the way I perform the calculation, I must upgrade each distinct calculation whenever a change is required (different numeric format, different numeric formula, etc.) If, on the other hand, I hide the calculation behind the interface of a callable module, then the calculation is coded only once With the help of a local module, the format_data procedure is transformed as shown
Trang 24a change to the npv function and then recompile No fuss, no muss
Notice that it doesn't make any sense to create npv as a standalone module outside of the format_data procedure This calculation is very specific to the format_data program Because no other module would ever call it, you needn't clutter up your stored procedure environment or your Oracle Forms program unit listing with this module-specific utility
I have found that few developers are aware of the ability to create local modules I have also found that these modules-within-a-module play an important role in allowing me to write well-structured, elegant programs
These days it seems that whenever I write a program with more than 20 lines, and with any
complexity whatsoever, I end up creating several local modules It helps me see my way through to a solution in the following ways:
● Conceptualizing my code at a higher level of abstraction by assigning a name to a whole
sequence of statements
● Performing top-down design and step-wise refinement of my requirements
● Extracting local modules at a later date, thus making truly independent, reusable procedures or functions if the need arises
Take a look at any of your more complex programs and I guarantee you will quickly identify
segments of the code that would serve you better bundled into a local module
Previous: 22.2 Build the
Most Functional Functions
Oracle PL/SQL Programming, 2nd Edition
Next: 22.4 Be Wary of Modules Without Any Parameters
22.2 Build the Most
Functional Functions
Book Index 22.4 Be Wary of Modules
Without Any Parameters
The Oracle Library
Navigation
Copyright (c) 2000 O'Reilly & Associates All rights reserved
Trang 25Previous: 22.3 Take Full
Advantage of Local
Modularization
Chapter 22Code Design Tips
Next: 22.5 Create Independent Modules
22.4 Be Wary of Modules Without Any Parameters
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: