Packages are the most important construct in
for building reusable code and plug-and-play components, and for employing object-oriented design techniques. As you become more comfortable with the language, you will find more of your time spent inside packages -- and using programs from packages built by other developers. This may be a very pleasant and rewarding experience -- if the packages are designed and implemented properly. If, on the other hand, you decide to build your packages in the same helter-skelter method (or lack thereof) I run into way too often, life out there in front of the monitor may get pretty miserable.
This chapter discusses some of the most important "best practices" for package construction and goes into detail on an effective coding style for packages. If you follow the ideas presented below, you have a very good chance of writing packages that are readable, maintainable, and enhanceable. It is also much more likely that other developers will find your packages usable and useful. You will find additional explanation regarding these practices, along with examples of the application of these practices, in the sections covering specific PL/Vision packages.
The following list offers a summary of the best practices for packages covered in this chapter:
Start with packages.
Get out of the habit of building standalone procedures and functions. Instead, start with a package.
Use a consistent and effective coding style.
Use comment lines as banners to delineate the different kinds of elements in the package. Employ a standard ordering for the elements of the package.
Choose appropriate and accurate names for both the package and the elements in the package.
As with any other kind of identifier, your package name should clearly communicate the point of the package. Avoid redundancies in the package name and its element names (such as
Organize package source code.
Come up with consistent file-naming conventions for the source code you stored in operating system files. Separate the package specification and body CREATE OR REPLACE statement into separate files.
Construct the optimal interface to your package.
Design your package so that it is easy -- and a pleasure -- to use. When you build packages for reuse, other
developers become your users. Treat them with respect. Make the parameters in your programs case-insensitive. Don't require users to know about and pass literal values.
Build flexibility directly into your packages.
Anticipate areas where options and flexibility will be required and then build them right in -- either with additional parameters or separate programs. Build toggles into your package so the behavior of the package can be changed without having to change the user's application.
Build windows into your packages.
Packages allow you to control tightly what a developer can see and affect inside the package. The flip side of this control is blindness and bewilderment. Your users can't figure out what is going on. Package windows allow for controlled read-only access to package data and activity.
Overload aggressively for smart packages.
Overloading modules means that you create more than one program with the same name. Overloading transfers the burden of knowledge from the user to the software. You do not have to try to remember the different names of the modules and their specific arguments. Properly constructed, overloaded modules will anticipate the different variations, hiding them behind a single name, and liberate your brain for other, more important matters.
Modularize the package body to create maintainable packages.
To build packages that are both immediately useful and enhanceable over the long run, you must develop a wicked allergy to any kind of code duplication inside the package. You need to be ready, willing, and able to create private programs in your package to contain all the shared code behind the public programs of the package.
Hide your package data.
Never define variables in the specification of a package (except when explicitly needed that way). Instead, declare them in the body and then provide a procedure to set the value of that variable, and a function to change the value of that variable (get-and-set). If you follow this practice, you will retain control over the values of, and access to, your package data. You can also then trace any reads from and writes to that data.
Construct multiple packages simultaneously.
As you build a program, be aware of both existing packages and the need for new areas or layers of functionality. Take the time to break off from development of program A to enhance packages B, C, and D -- and then apply those new features back into A. It might take a little bit longer to finish your first program, but when you are done you will have strengthened your overall
development environment and increased your volume of reusable code.
Late in July 1996, I received this note from one of my technical reviewers, John M. Beresniewicz:
I've built half a dozen pretty hefty packages, and still I find myself wondering at the start of implementing some new functionality: how should I do this? I think packages are intimidating developers out there (maybe I'm wrong) and part of the reason may be that it is very hard to decide what to put where and why. It seems like most of my packages start with an idea that becomes a
stored procedure. (All initial experiments are named with the prefix
to let me know they are part of my playground.) As soon as the procedure becomes more than 100 lines long or contains code duplication or a related but different procedure suggests itself or needs to stash some persistent data, a package is magically born.
Once spawned, packages often have a life of their own, they grow and mature and sometimes die or are subsumed by larger packages. I don't know if there is an idea here, but something that makes deciding what and how to start a package may help developers... I suppose the whole book is just that in a sense.
There is definitely an idea in there, but my perspective is somewhat simpler than what John probably had in mind: Get out of the habit of building standalone procedures and functions. Instead, start with a package! It is certainly the case that most complex programs eventually mutate into or are absorbed by packages. There is nothing wrong with that evolutionary process. You can, however, save yourself some trouble by creating a package to hold that seemingly simple and lonely procedure or function.
If you start with a package, several benefits accrue:
You immediately work at a higher level of abstraction. You think of your single program as just one component of a whole range of related functionality. In the first implementation of the package you may not be aware of even one other program for this package, but you are allowing for the possibility of such programs.
Related to the level of abstraction, you find yourself creating separate layers and partitions of functionality. Then, as you work on additional programs, you identify the appropriate package for that program. Lo and behold, you find things falling into place. You realize that everything has a place. Your code takes on an elegant sense of internal organization that makes it easier to use and understand.
From the beginning, all calls to that program employ the dot notation necessary (and, I would argue, inevitable) to reference a package-based element. You don't have to go back later and change those calls or create another layer of code to support backward compatibility.
You will never regret the minuscule amount of extra time required to encapsulate your standalone programs inside a package.
Copyright (c) 2000 O'Reilly & Associates. All rights reserved.