Object Syntax Introduction
Visual Basic has had powerful object-oriented
capabilities since the introduction of version 4.0. VB.NET carries that
tradition forward. VB.NET simplifies some of the syntax and greatly enhances
these capabilities, and now supports the four major defining concepts required
for a language to be fully object-oriented:
q
Abstraction
VB has supported
abstraction since VB4. Abstraction is merely the ability of a language to
create "black box" code to take a concept and create an abstract
representation of that concept within a program. A Customer
object, for instance, is an abstract representation of a real-world customer. A
Recordset
object is an abstract representation of a set of data.
q
Encapsulation This has also been with us since version
4.0. It's the concept of a separation between interface and implementation. The
idea is that we can create an interface (Public methods in a
class) and, as long as that interface remains consistent, the
application can interact with our objects. This remains true even if we
entirely rewrite the code within a given method thus the interface is
independent of the implementation.
Encapsulation allows us to hide the internal implementation details of a class.
For example, the algorithm we use to compute Pi might be proprietary. We can
expose a simple API to the end user, but we hide all of the logic used by our
algorithm by encapsulating it within our class.
q
Polymorphism
Likewise,
polymorphism was introduced with VB4. Polymorphism is reflected in the ability
to write one routine that can operate on objects from more than one class
treating
different objects from different classes in
exactly the same way. For instance, if both Customer
and Vendor
objects have a Name
property, and we can write a routine that calls the Name
property regardless of whether we're using a Customer
or Vendor
object, then we have polymorphism.
VB, in fact, supports polymorphism in two ways through late binding (much
like Smalltalk, a classic example of a true object-orientated language) and
through the implementation of multiple interfaces. This flexibility is very
powerful and is preserved within VB.NET.
q
Inheritance
VB.NET is the first
version of VB that supports inheritance. Inheritance is the idea that a class
can gain the pre-existing interface and
behaviors of an existing class. This is done by inheriting these behaviors from
the existing class through a process known as subclassing. With the
introduction of full inheritance, VB is now a fully object-orientated
language by any reasonable definition.
We'll discuss these concepts in detail in Chapter 7, using
this chapter and Chapter 6 to focus on the syntax that enables us to utilize
these concepts.
Additionally, because VB.NET is a component-based language,
we have some other capabilities that are closely related to traditional
concepts of object-orientation:
q
Multiple interfaces
Each class in VB.NET defines a primary interface (also called the default or
native interface) through its Public methods, properties and events. Classes
can also implement other, secondary interfaces in addition to this primary
interface. An object based on this class then has multiple interfaces, and a
client application can choose by which interface it will interact with the
object.
q
Assembly (component) level scoping
Not only can we define our classes
and methods to be Public (available to anyone), Protected
(available through inheritance) and Private
(only available locally), but we can also define them
as Friend
meaning they are only available within the current assembly or component.
This is not a traditional object-oriented concept, but is very powerful when
designing component-based applications.
In this chapter we'll explore the creation and use of
classes and objects in VB.NET. In Chapter 6, we'll examine inheritance and how
it can be used within VB.NET. In Chapter 7, we'll explore object-oriented
programming in depth, fully defining the features listed and exploring how we
can use these concepts.
Before we get too deep into code, however, it is important
that we spend a little time familiarizing ourselves with basic object-oriented
terms and concepts.
To start with, let's take a look at the word object
itself, along with the related class
and instance
terms. Then we'll move on to discuss the four terms that define the major
functionality in the object-oriented world encapsulation, abstraction,
polymorphism, and inheritance.
An object
is a code-based abstraction of a real-world entity or
relationship. For instance, we might have a Customer
object that represents a
real-world customer such as customer number 123 or we might have a File
object that represents C:\config.sys on our computer's hard drive.
A closely related term is class.
A class is the code that defines our object, and all objects are created based
on a class. A class is an abstraction of a real-world concept, and it provides
the basis from which we create instances of specific objects. For example, in
order to have a Customer object representing customer number 123, we
must first have a Customer class that contains all of the code (methods,
properties, events, variables, and so on) necessary to create Customer
objects. Based on that class, we can create any number of objects each one an
instance
of the class. Each object is identical to the others except that it may
contain different data.
We may create many instances of Customer
objects based on the same Customer
class. All of the Customer
objects are identical in terms of what they can do and the code they contain,
but each one contains its own unique data. This means that each object
represents a different physical customer.
Composition of an Object
We use an interface
to get access to an
object's data and behavior. The object's data and behaviors are contained
within the object, so a client application can treat the object like a black
box accessible only through its interface. This is a key object-oriented
concept called encapsulation. The idea is that any programs that make use of this
object won't have
direct access to the behaviors or data but rather those programs must make
use of our object's interface.
Let's walk through each of the three elements in detail.
Interface
The interface is defined as a set of methods (Sub
and Function
routines), properties (Property
routines), events, and
fields (variables or attributes) that
are declared Public
in scope.
The word attribute
means one thing in the general object-oriented world, and something else in
.NET. The OO world often refers to an object's variables as attributes, while
in .NET an attribute is a coding construct that we can use to control
compilation, the IDE, and so on.
We can also have Private
methods and properties in our code. While these methods can be called by code
within our object, they are not part of the interface and cannot be called by
programs written to use our object. Another option is to use the Friend keyword, which defines the scope to be our
current project, meaning that any code within our project can call the method,
but no code outside of our project (that is, from a different .NET assembly)
can call the method. To complicate things a bit, we can also declare methods
and properties as Protected, which are available to classes that inherit from our
class. We'll discuss Protected in Chapter 6 along with inheritance.
For example, we might have the following code in a class:
Public Function CalculateValue() As Integer
End Function
Since this method is declared with the Public
keyword, it is part of our interface and can be called by client applications
that are using our object. We might also have a method such as this:
Private Sub DoSomething()
End Sub
This method is declared as being Private
and, so, it is not part of our interface. This method can only be called by
code within our class not by any code outside of our class, such as the code
in a program that is using one of our objects.
On the other hand, we can do something like this:
Public Function CalculateValue() As Integer
DoSomething()
End Function
In this case, we're calling the Private
method from within a Public method. While code using our objects can't directly
call a Private
method, we will frequently use Private
methods to help structure the
code in our class to make it more maintainable and easier to read.
Finally, we can use the Friend
keyword:
Friend Sub DoSomething()
End Sub
In this case, the DoSomething method can
be called by code within our class, or from other classes or modules within our
current VB.NET project. Code from outside our project will not have access to
the method.
The Friend scope is very similar to the Public scope, in that it makes methods available for use
by code outside of our object itself. However, unlike Public, the Friend keyword restricts access to code within our current VB.NET project
preventing code in other .NET assemblies from calling the method.
This is very unlike the C++ friend keyword, which implements a form of tight coupling
between objects and which is generally regarded as a bad thing to do. Instead,
this is the same Friend keyword that VB has had for many years and which was later adopted by
Java to provide component-level scoping in that language as well. It is
equivalent to the internal keyword in C#.
Implementation or Behavior
The code inside of a method is called the implementation.
Sometimes it is also called behavior
since it is this code that
actually makes the object do useful work.
For instance, we may have an Age
property as part of our object's interface. Within that method, we may have
some code (perhaps written by an inexperienced developer, since it is just
returning a non-calculated value):
Private mintAge As Integer
Public ReadOnly Property Age() As Integer
Get
Return mintAge
In this case, the code is returning a value directly out of
a variable, rather than doing something better like calculating the value based
on a birth date. However, this kind of code is often written in applications,
and it seems to work fine for a while.
The key concept here is to understand that client
applications can use our object even if we change the implementation as long
as we don't change the interface. As long as our method name and its parameter
list and return data type remain unchanged, we can change the implementation
all we want.
The code necessary to call our Age
property would look something like this:
The result of running this code is that we get the Age
value returned for our use. While our client application will work fine, we'll
soon discover that hard coding the age into the application is a problem and
so, at some point, we'll want to improve this code. Fortunately, we can change
our implementation without changing the client code:
Private mdtBirthDate As Date
Public
ReadOnly Property Age() As Integer
Get
Return DateDiff(DateInterval.Year, mdtBirthDate, Now())
End Get
End
Sub
We've changed the implementation behind the interface
effectively changing how it behaves without changing the interface itself.
Now, when our client application is run, we'll find that the Age
value returned is accurate over time where, with the previous implementation,
it
was not.
It is important to keep in mind that encapsulation is a
syntactic tool it allows our code to continue to run without change. However,
it is not semantic meaning that, just because our code continues to run,
doesn't mean it continues to do what we actually wanted it to do.
In this example, our client code may have been written to
overcome the initial limitations of the implementation in some way, and thus
might not only rely on being able to retrieve the Age
value, but the client code might be counting on the result of that call being a
fixed value over time.
While our update to the implementation won't stop the client
program from running, it may very well prevent the client program from running
correctly.
Member or Instance Variables
The third key part of an object is its data, or state.
In fact, it might be argued that the only important part of an object is its
data.
After all, every instance of a class is absolutely identical in terms of its
interface and its implementation the only thing that can vary at all is the
data contained within that particular object.
Member variables are those declared so that they are available to all code
within our class. Typically member variables are Private in scope available only to the code in our class
itself. They are also sometimes referred to as instance variables or as attributes. The .NET
Framework also refers to them as fields.
We shouldn't confuse instance variables with properties.
In VB, a Property
is a type of method that is geared around retrieving and setting values, while
an instance variable is a variable within the class that may hold the
value exposed by a Property.
For instance, we might have a class that has instance
variables:
Public Class TheClass
Private mstrName As String
Private mdtBirthDate As Date
End Class
Each instance of the class each object will have its own
set of these variables in which to store data. Because these variables are
declared with the Private keyword, they are only available to code within
each specific object.
While member variables can
be declared as Public
in scope, this makes them available to any code using our objects in a manner
we can't control. Such a choice directly breaks the concept of encapsulation,
since code outside our object can directly change data values without following
any rules that might otherwise be set in our object's code.
If we want to make the value of an instance variable
available to code outside of our object, we should use a property:
Public Class TheClass
Private mstrName As String
Private mdtBirthDate As Date
Public ReadOnly Property Name() As String
Get
Return mstrName
End Get
End
Property
End Class
Since the Name
property is a method, we are not
directly exposing our internal variables to client code, so we preserve
encapsulation of our data. At the same time, through this mechanism we are able
to safely provide access to our data as needed.
Member variables can also be declared with Friend
scope which means they are
available to all code in our project. Like declaring them as Public,
this breaks encapsulation and is strongly discouraged.
Now that we have a grasp on some of the basic
object-oriented terminology, we're ready to explore the creation of classes and
objects. First, we'll see how VB allows us to interact with objects, and then
we'll dive into the actual process of authoring those objects.