Forum            

This article will focus on defining the concepts of object orientation as they relate to software development in general.

There have been more than a few books written on object-oriented programming, so this article will not attempt to deliver a full treatise on a subject well deserving of hundreds of pages. Instead, we will cover only the ground that we need to cover so that programmers new to object-oriented programming and programmers with no OOP experience at all will have a good backdrop of knowledge for exploring the .NET Framework Class Library.

In years past, many developers have debated whether Visual Basic was an object-oriented language. Instead of investigating any of these prior claims, arguments, or discussions, let's focus instead on the here and now. Visual Basic .NET supports the major traits of an object-oriented language, including the capability to:

Wrap data and behavior together into packages called classes (this is a trait known as encapsulation)

Define classes in terms of other classes (a trait known as inheritance)

Override the behavior of a class with a substitute behavior (a trait known as polymorphism)

We'll examine each one of these traits in detail. We'll also examine ways in which you will see these concepts at work inside of the .NET Framework.

Classes—Wrapping Data and Behavior Together

Classes are blueprints or specifications for actual objects that we will create in our code. They define a standard set of attributes and behaviors. Because classes only define a structure or intent, they are virtual in nature. For instance, a class cannot hold data, it can't receive a message, and in fact can't do any processing at all. This is because classes are only meant to be object factories. Just like real engineering blueprints of a building, they only exist to construct something else. When we program, this "something else" we are trying to construct is an object. An object can hold data, can receive messages, and can actually carry out processing.

While you don't typically use the term class in your everyday (non-programming) life, we are all certainly familiar with the concept of objects. These are the things that surround us day in and day out; they are the nouns in our universe. We are used to interacting with objects. For example, you place a plate on your table for dinner. The plate has food on it—a few different types of food, in fact. We can see that all of these things have distinct properties: The plate is white with a faint flower pattern, and the food has a particular texture, taste, and smell. We also expect that objects will allow us to interact with them in different ways.

Just like in the real world, code objects (we also call them instances) are actual physical manifestations of classes.

Classes as Approximations
If we discuss classes in the context of programming, we say that they establish a template for objects by defining a common set of possible procedures and data. Procedures are used to imbue the class with a set of behaviors; when implemented in a class they are called methods. Classes maintain data inside of properties (which may or may not be visible to other classes). Behaviors are the verbs of classes, and properties are the nouns. A car, for instance, will accelerate in a prescribed fashion. This would be a behavior. A car will also have a specific weight, color, length, and so on. These are properties. From a technical, implementation point of view, there is actually no difference between the way that methods and properties are implemented. They both have function signatures, and both execute some body of code. In addition, both of them can accept parameters and return values.

Note

There are some general guidelines for when to use properties versus methods (and vice versa), but probably the best advice is to just be consistent. Most of the time, these rules will help steer you to the correct decision:

--Use a method if you are going to be passing in more than a few parameters.

--If you find yourself writing a method called GetXXX or SetYYY, chances are good this should be a property instead.

--Methods are more appropriate than properties if there will be many object instantiations or inter-object communication inside of the function.

--Properties, when implemented, should be stateless with respect to one another. In other words, a property should not require that another property be set before or after it is set. If you find this kind of dependency inside of a property, it should probably be a method instead.

Classes are typically constructed to mimic, or approximate, real-world physical structures or concepts. By using classes in your code, you can simplify both your architecture and your understanding of the code; this is due to the inherent approachability of objects—your mind is used to deal with objects. For example, which do you suppose would make more intuitive sense to you?

You are writing code to move an icon from the left side of the screen to the right side of the screen. The procedural programming way would probably have you calling some API function (maybe it's called SystemDskRsrcBlit) and passing parameters into the function call. But, what if you were free to do this:

--Create an icon object

--Tell it to MoveLeft

The object-oriented way just seems to make more sense to us—it seems to appeal to the way that our minds are wired.

Note

The difference between the system that we are programming and the real-world process that we are modeling is often referred to as the semantic gap. You could summarize some of what we have been talking about here by saying that object- oriented programming aims to reduce the semantic gap between programming and the real world.

Of course, just because the basic premises of objected-oriented programming are simple to understand doesn't mean that the actual programming of object-oriented systems is trivial. Once you can work your way through the syntax and condition yourself to think in an object-like fashion while actually designing your applications, some of the perceived complexity associated with software development will begin to fade.

Talking Between Classes
We have said that classes define a set of behaviors. These behaviors would be useless to us unless we had a way to actually stimulate or initiate a particular behavior. Therefore, we have the concept of messaging. A message is nothing more than a request, from one object to another, to perform some sort of action. The receiving object may choose to ignore the action (especially if it doesn't have a behavior defined that would map to the requested action) or it may perform a specific action that could, in turn, send messages to other objects.

A core tenet of object-oriented systems is that classes think for themselves. A particular class knows how it should react to an incoming message; the calling class isn't forced to understand how or why the receiving class behaves the way that it does. This is the essence of information hiding. In information hiding, an object hides its internal machinations from other objects (see Figure 3.1). Information hiding is important because it helps reduce the overall design complexity of an application. In other words, if Class A doesn't have to implement code to understand how Class B operates, we have just reduced the complexity of the code.

As programmers, we initiate a message from one class to another by calling a method or property on the target class. Part of this message that we send encapsulates any parameters or data needed by the receiving class to execute the action.

Thus, we have classes in an object-oriented programming environment. A physical manifestation of a class in the programming world consists of code that defines these attributes and behaviors through property and method routines.

In this book, our focus on the Framework Class Library will introduce you to new classes in each chapter. They will exhibit all of the traits and characteristics of the classes that we have just defined.

Now, let's move on and discuss the next OO trait of Visual Basic .NET—inheritance.


Inheritance—Defining Classes in Terms of One Another
Inheritance is the capability for one class to inherit or take on the traits of another class. Typically, this happens in a hierarchical fashion. Consider the following simple example to see how this inheritance results in a natural hierarchy of classes. Figure 3.2 shows three classes: HR Employee, IT Employee, and Warehouse Employee. Each of these is shown with some of their properties and methods.

By looking at them, it quickly becomes clear that we could hierarchically structure these classes by abstracting their common traits into a parent class. These three classes would then be child classes of that one parent class. Figure 3.3 shows how this results in a tree structure for our classes. Another way to think about this is called sub-typing. Children classes can often be thought of as different "types" of the parent class (an HR employee is a type of Employee, and so on).

This hierarchical structure is typical of well-engineered class libraries—the Framework Class Library is organized in just such a way.

Note

If you examine Figure 3.3, you will see that our arrows point from the child classes to the parent class. This may not seem intuitive to you; after all, aren't we creating a child class from a parent class? This notation is advocated because it shows that the child knows about the parent, but the parent does not (necessarily) know about the child.

Inheritance by Natural Relationship
One of the nice things about inheritance is that it often simply realizes a relationship that we already make in our minds. That is, it is often just a recognition of real-world relationships. We can tell that a dog or a cat is a type of an animal—the inheritance between the concept of an animal class and a dog or cat class is obvious. Again, this is a good thing as it helps to reduce the semantic gap that we talked about earlier and helps you make your code organization easier to understand. Organizing your code is only one benefit of inheritance—code reuse is another.

We have said that a class can inherit the traits of another class. We have also said that the traits of a class are implemented as property and method routines. When these routines are inherited between classes, it obviously means we are assuming the actual source code of one class into another. Thus, we have code reuse.

If we look back at Figure 3.3 we can see how each line of code that was written to implement the parent class methods can be reused by each of the child classes. Code reuse in this fashion becomes a powerful rapid application development enabler. If we needed to change some lines of code in one of the parent class methods, the change would be immediately realized in its children classes. This also allows us to build up complexity in a child class by inheriting from potentially simple base classes.

Note

There are many different ways to express the inheritance relationship: parent to child, super-class to sub-class, ancestor class to descendant class, generalized class to specialized class, and so on. In this book, anytime we use these terms you should know that we are just referring back to this basic concept of inheritance relationship.

Figure 3.4 shows class nomenclature, in ancestor/descendant terms, against a class tree.

Figure 3.4 Inheritance nomenclature examples.

We now know that identifying logical relationships between objects will help us out in the area of code reuse. But the examples we have talked about so far have been based on relationships between objects—an appraisal of one object being a type of another object. If, however, you approach inheritance by first looking at its end result, you'll find that you can end up with an entirely different perspective. Let's look at an example: Let's say that we have a class that defines operations for a specific type of printer. We'll call this class InkJet. Intuitively, we sense a parent class that would most likely be called Printer. Introducing a Printer parent class produces the inheritance that we see in Figure 3.5.

Inheriting from the Printer class is a good solution for us because it already defines some basic operations (line feed, paper out, and so on) that we can use as building blocks for our InkJet class. At the same time, we will add some of our own behaviors that are specific to inkjet printers. But what if we had the requirement for some low-level communication code that would send an error signal across a parallel port? Also, what if that code was already available to us in yet another class?

Figure 3.6 shows how we could inherit from a fictional ParallelPort object to leverage the SendErrorSignal code that we need.

Figure 3.5 Inheritance based on relationship.

This is subtly different from what we were doing before because it is very difficult to envision a logical relationship between a parallel port object and an inkjet object. After all, an inkjet printer is not a type of a parallel port—there is no obvious hierarchical relationship to draw between the two. In this case, we would be implementing inheritance to get at raw code reuse. This doesn't do anything for us in terms of making our code easier to understand—it does not reinforce a relationship between abstract classes and real-world objects.

Note

Inheriting for pure code reuse in the absence of a sub-type relationship is certainly something that you can do with classes, but isn't always the best approach. You gain code reuse at the expense of increased complexity in your system (and therefore, a corresponding increase in the effort required to understand your system). In .NET, we advocate implementing an interface instead of using class inheritance to represent this relationship; you still get the desired code reuse without complicating your class relationships.

What if you decided to proceed ahead with class inheritance, and decided to inherit from both the Printer and the ParallelPort class? This is called multiple inheritance. Multiple inheritance is not supported by the .NET runtime.


www.InfromIt.com

Microsoft

Khurram Hanif
Hosted by www.Geocities.ws

1