Principles of Object-Oriented Programming

It is often said that there are four main concepts in the area of object-oriented programming:

  • Abstraction

  • Encapsulation

  • Inheritance

  • Polymorphism

Each of these concepts plays a significant role in VB.NET programming at one level or another. Encapsulation and abstraction are “abstract” concepts providing motivation for object-oriented programming. Inheritance and polymorphism are concepts that are directly implemented in VB.NET programming.

Abstraction

Simply put, an abstraction is a view of an entity that includes only those aspects that are relevant for a particular situation. For instance, suppose that we want to create a software component that provides services for keeping a company’s employee information. For this purpose, we begin by making a list of the items relevant to our entity (an employee of the company). Some of these items are:

  • FullName

  • Address

  • EmployeeID

  • Salary

  • IncSalary

  • DecSalary

Note that we include not only properties of the entities in question, such as FullName, but also actions that might be taken with respect to these entities, such as IncSalary, to increase an employee’s salary. Actions are also referred to as methods, operations, or behaviors. We will use the term methods, since this term is used by VB.NET.

Of course, we would never think of including an IQ property, since this would not be politically correct, not to mention discriminatory and therefore possibly illegal. Nor would we include a property called HairCount, which gives the number of hairs on the employee’s right arm, because this information is of absolutely no interest to us, even though it is part of every person’s being.

In short, we have abstracted the concept of an employee — we have included only those properties and methods of employees that are relevant to our needs. Once the abstraction is complete, we can proceed to encapsulate these properties and methods within a software component.

Encapsulation

The idea of encapsulation is to contain (i.e., encapsulate) the properties and methods of an abstraction, and expose only those portions that are absolutely necessary. Each property and method of an abstraction is called a member of the abstraction. The set of exposed members of an abstraction is referred to collectively as the public interface (or just interface) of the abstraction (or of the software component that encapsulates the abstraction).

Encapsulation serves three useful purposes:

  • It permits the protection of these properties and methods from any outside tampering.

  • It allows the inclusion of validation code to help catch errors in the use of the public interface. For instance, it permits us to prevent the client of the employee software component from setting an employee’s salary to a negative number.

  • It frees the user from having to know the details of how the properties and methods are implemented.

Let us consider an example that involves the Visual Basic Integer data type, which is nicely encapsulated for us by VB. As you undoubtedly know, an integer is stored in the memory of a PC as a string of 0s and 1s called a binary string. In Visual Basic, integers are interpreted in a form called two’s-complement representation, which permits the representation of both negative and non-negative values.

For simplicity, let us consider 8-bit binary numbers. An 8-bit binary number has the form a7a6a5a4a3a2a1a0, where each of the axs is a 0 or a 1. We can think of it as appearing in memory as shown in Figure 4-1.

An 8-bit binary number

Figure 4-1. An 8-bit binary number

In the two’s-complement representation, the leftmost bit, a7 (called the most significant bit), is the sign bit. If the sign bit is 1, the number is negative. If the sign bit is 0, the number is positive.

The formula for converting a two’s-complement representation a7a6a5a4a3a2a1a0 of a number to a decimal representation is:

decimal rep. = -128a7 + 64a6 + 32a5 + 16a4 + 8a3 + 4a2 + 2a1 + a0

To take the negative of a number when it is represented in two’s-complement form, we must take the complement of each bit (that is, change each 0 to a 1 and each 1 to a 0) and then add 1.

At this point you may be saying to yourself, “As a programmer, I don’t have to worry about these details. I just write code like:

x = -16
y = -x

and let the computer and the programming language worry about which representation to use and how to perform the given operations.”

This is precisely the point behind encapsulation. The details of how signed integers are interpreted by the computer (and the compiler), as well as how their properties and operations are implemented, are encapsulated in the integer data type itself and are thus hidden from us, the users of the data type. Only those portions of the properties and operations that we need in order to work with integers are exposed outside of the data type. These portions form the public interface for the Integer data type.

Moreover, encapsulation protects us from making errors. For instance, if we had to do our own negating by taking Boolean complements and adding 1, we might forget to add 1! The encapsulated data type takes care of this automatically.

Encapsulation has yet another important feature. Any code that is written using the exposed interface remains valid even if the internal workings of the Integer data type are changed for some reason, as long as the interface is not changed. For instance, if we move the code to a computer that stores integers in one’s-complement representation, then the internal procedure for implementing the operation of negation in the integer data type will have to be changed. However, from the programmer’s point of view, nothing has changed. The code:

x = -16
y = -x

is just as valid as before.

Interfaces

As VB programmers, we must implement encapsulation through the use of software components. For instance, we can create a software component to encapsulate the Employee abstraction discussed earlier.

In VB.NET, the methods of an interface are realized as functions. On the other hand, a property, as we see later in this chapter, is realized as a private variable that stores the property’s value together with a pair of public functions — one to set the variable and one to retrieve the variable. These functions are sometimes referred to as accessor methods of the property. It is the set of exposed functions (ordinary methods and accessor methods) that constitute the interface for an abstraction.

In general, a software component may encapsulate and expose more than one abstraction — hence, more than one interface. For example, in a more realistic setting, we might want a software component designed to model employees to encapsulate an interface called IIdentification (the initial “I” is for interface) that is used for identification purposes. This interface might have properties such as name, Social Security number, driver’s license number, age, birthmarks, and so on. Moreover, the software component might also encapsulate an interface called IEducation for describing the employee’s educational background. Such an interface might implement properties such as education level, degrees, college attended, and so on.

The interface of each abstraction exposed by a software component is also referred to as an interface of the software component. Thus, the Employee component implements at least two interfaces: IIdentification and IEducation. Note, however, that the term interface is often used to refer to the set of all exposed properties and methods of a software component, in which case a component has only one interface.

Referring to our original Employee abstraction, its interface might consist of the functions shown in Table 4-1. (Of course, this interface is vastly oversimplified, but it is more than sufficient to illustrate the concepts.)

Table 4-1. Members of the Employee interface

Type

Name

Property

FullName: GetFullName(), SetFullName( )

Property

Address: GetAddress(), SetAddress( )

Property

EmployeeID: GetEmployeeID(), SetEmployeeID( )

Property

Salary: GetSalary(), SetSalary( )

Method

IncSalary( )

Method

DecSalary( )

Using the term interface as a set of functions, while quite common, poses a problem. Just listing the functions of the interface by name (as done previously) does not provide enough information to call those functions. Thus, a more useful definition of interface would be the set of signatures of the public functions of a software component.

To clarify this, let us discuss one of the most important distinctions in object- oriented programming — the distinction between a function declaration and an implementation of that function.

By way of example, consider the following sorting function:

Function Sort(a(  ) as Integer, iSize as Integer) as Boolean
   For i = 1 to iSize
      For j = i+1 to iSize
         If a(j) < a(i) Then swap a(i), a(j)
      Next j
   Next I
   Sort = True
End Function

The first line in this definition:

Function Sort(a(  ) as Integer, iSize as Integer) as Boolean

is the function declaration. It supplies information on the number and types of parameters and the return type of the function. The body of the function:

For i = 1 to iSize
   For j = i+1 to iSize
      If a(j) < a(i) Then swap a(i), a(j)
   Next j
Next i
Sort = True

represents the implementation of the function. It describes how the function carries out its intended purpose.

Note that it is possible to alter the implementation of the function without changing the declaration. In fact, the current function implementation sorts the array a using a simple selection-sort algorithm, but we could replace that sorting method with any one of a number of other methods (bubble sort, insertion sort, quick sort, and so on).

Now consider a client of the Sort function. The client only needs to know the function declaration in order to use the function. It need not know (and probably doesn’t want to know) anything about the implementation. Thus, it is the function declaration, and not the implementation, that forms the interface for the function.

The signature of a function is the function name and return type, as well as the names, order, and types of its parameters. A function declaration is simply a clear way of describing the function’s signature. Note that Microsoft does not consider the return type of a function to be part of the function’s signature. By signature, they mean what is generally termed the function’s argument signature. The reasons for doing this become clearer later in the chapter when we discuss overloading, although it would have been better (as usual) if they were more careful with their terminology.

Under this more specific definition of interface, the interface for our employee component might be as follows (in part):

Function GetFullName(lEmpID As Long) As String
Sub SetFullName(lEmpID As Long, sName As String)
. . .
Sub IncSalary(sngPercent As Single)
Sub DecSalary(sngPercent As Single)

Get VB.NET Language in a Nutshell, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.