As the name suggests, encapsulation means that a unit of functionality is enclosed to such a degree that you are able to extract and use that functionality in a physically different environment. The unit of functionality contains everything it requires, and provided that the correct inputs can be applied, it will function in exactly the same way in any environment. The degree of encapsulation can also be expressed in terms of how the unit couples. A unit of functionality that is dependent on many external conditions is said to be tightly coupled. The converse is true of a loosely coupled unit. As a programmer, you'll no doubt have tried at least once to extract and reuse a block of code that had so many dependencies that it was easier to rewrite the code to fit your needs than to reuse it. This difficulty is a direct consequence of a tightly coupled unit and serves to highlight the importance of coupling when designing or coding a unit. The fact that an experienced programmer will understand the issues involved with coupling is one reason why experienced and disciplined programmers are valuable assets to a company.
The second key attribute of reuse has to do with how generic a piece of functionality is. If you want to use some functionality elsewhere, it stands to reason that the functionality should be able to apply to a number of different situations. Imagine a function written to take a date input and to check that the date is valid and in the range 3/2/1998 through 5/10/1998. The code below should help you visualize this.
Function IsDateOK(DateIn As Date) As Boolean If DateIn >= "3/2/1998" And DateIn <= "5/10/1998" Then IsDateOK = True Else IsDateOK = False End If End Function
This function is clearly reusable because it has no external dependencies; that is, it doesn't call any other procedures within the application. In terms of actual reuse, however, it's pretty useless unless you specifically want to compare against the same date range in every instance in which you use this function. The function would be far more useful if you were to add inputs for a minimum and a maximum date because the function could then check whether a date is valid and falls between ranges specified by the routine calling the function. Here is an amended version of the same function; this time, it's more useful because it's generic enough to be applied in a number of situations:
Function IsDateOK(DateIn As Date, MinDate As Date, _ MaxDate As Date) As Boolean If DateIn >= MinDate And DateIn <= MaxDate Then IsDateOK = True Else IsDateOK = False End If End Function
For a piece of functionality to be reusable, it doesn't have to be totally decoupled from other routines in the application; in fact, doing so would make reuse impractical. What you must keep in mind, however, is that a unit of functionality is truly reusable only if all of its dependencies are present. To achieve good reusability, you need to set the boundaries or scope of a unit's functionality. For example, a reusable unit might be a single procedure, a collection of procedures, or an entire application. As long as the unit is not coupled to anything outside its bounds, it will be reusable. In programming terms, this means reducing the number of global (for Visual Basic 3) or public variables. In the days of Visual Basic 3, one of the recommended practices was to place global variables in a standard (BAS) module. Programmers should have dropped this practice with Visual Basic 4 because of the ability to encapsulate variables within class or form module properties. However, even in Visual Basic 4, constants still had to be made public because class and form objects couldn't expose constants as part of their interface. One way around this was to write type libraries. But type libraries, being external components, are contrary to encapsulation and can be difficult to maintain across multiple projects or components. To avoid public constants, Visual Basic 5 introduced a feature named enumerated constants, which allows constants to be exposed as part of the interface of the component to which they belong. This addition enables you to completely encapsulate a component. (We'll explain enumerated constants in more detail later in this chapter.)
One of the most widely used bad programming practices is to link areas of functionality with variables of global scope. Imagine an application with a modest 30 public variables. Any unit that couples to those variables is guaranteed to be difficult to reuse in another application because you'll have to recreate these variables and their values. Even in the same application, the state of global variables will change and might depend on certain event sequences, sometimes complex, being executed. Chapter 2 indicates some circumstances in which you can get components to share the same global data, with unfortunate results. And Chapter 4 includes even more on why you shouldn't use global variables.