CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Eric Wise

Business & .NET

Object Oriented Programming VB .NET - Level 000

I have recently been tasked with creating a 1,000 ft overview of Object Oriented concepts in the VB .NET world for a development staff whose experience is mostly in old school ASP and VB 6.0

Much credit to The Book of Visual Studio .NET, ISBN 1-886411-69-7 from No Starch Press written by Robert B. Dunaway which I used as a template for the document and borrowed some examples from.

What is an object?

In its most basic form, an object simply is an entity that exposes properties, methods, and events.  Objects provide abstraction and encapsulation to make our job as programmers easier by exposing code in ways that are easy for a human being to organize and comprehend.

 

Abstraction

Abstraction is the easiest of the OOP concepts to understand and is often something we implement naturally without realizing it. In short, abstraction is the implementation of code to mimic the actions and characteristics of any realworld entity.

The most commonly-used example for describing abstraction is the abstraction of a person. Imagine that we want to create an object from a class that represents a person. A person class will need to describe its characteristics through the implementation of properties (height, weight, eye color, etc).  Actions of the person class are performed by methods (Speak, Eat, Sleep, etc).

 

Encapsulation

We expose properties and methods through abstraction, but we implement the actual workings of our component through encapsulation. A few encapsulated actions might include data access, data validation, calculations, adding data to an array or collection, or calling other methods or other components. Exposing our component's interface while hiding the component's implementation code effectively separates interface implementation from our black box implementation. This separation helps to modularize components to perform a more specific task while requiring minimal knowledge of how the black box actually works.

One of the more useful applications of encapsulation is in making a complex component. For example, your program may require interaction with a third party system, but interaction with this system can only be achieved through a complex API. Rather than requiring all developers on a project to spend valuable time figuring out how to correctly use the third party API or even find ways to misuse it, one developer could study the API then encapsulate it in a component that exposes a less complex interface. This is a common practice that saves time and reduces potential bugs.  The .NET Framework itself encapsulates various Windows APIs.

 

Polymorphism

Polymorphism is the ability to implement the interface of another class into multiple classes or to implement multiple interfaces on a single class. This method of implementation is referred to as interface-based programming. A vehicle is a good example of polymorphism. A vehicle interface would only have those properties and methods that all vehicles have, a few of which might include paint color, number of doors, accelerator, and ignition. These properties and methods would apply to all types of vehicles including cars, trucks, and semi-trucks.

Polymorphism will not implement code behind the vehicle's properties and methods. (That's the job of inheritance covered in the next section.) Instead, polymorphism is the implementation of an interface. If the car, truck, and semitruck all implement the same vehicle interface, then the client code for all three classes can be exactly the same.

Implementing the vehicle interface only requires the declaration of properties and methods. To create a new interface, use the Interface keyword in place of the Class keyword. The client implementing the new interface can do so by using the Implements keyword as shown in the example:

Public Interface IVehicle
    Property PaintColor() As String
    Property NumberOfDoors() As Integer
 
    Function Ignition() As Boolean
End Interface
 
Public Class Car
    Implements IVehicle
 
    Public Function Ignition() As Boolean Implements IVehicle.Ignition
 
    End Function
 
    Public Property NumberOfDoors() As Integer Implements IVehicle.NumberOfDoors
        Get
 
        End Get
        Set(ByVal Value As Integer)
 
        End Set
    End Property
 
    Public Property PaintColor() As String Implements IVehicle.PaintColor
        Get
 
        End Get
        Set(ByVal Value As String)
 
        End Set
    End Property
End Class

 

Inheritance

Inheritance is the ability to apply another class's interface and code to your own class. Remember, with polymorphism, you got the interface; however, you must apply your own code. The power of inheritance is the ability to inherit code, saving developers time. This type of inheritance is called implementation inheritance. To inherit another class, use the Inherits keyword.

In the domain manager pattern, objects are inherited from a base class called DomainObject.  This means that every object in the system, whether it is a student, teacher, building, address, or any other object type a system requires will return True if asked if it is a Domain Object.  The power of this type of inheritence is that it allows us to design generic functions that take an object of the type DomainObject and route them appropriately to specialized code to deal with them.  This is used in the Domain Manager's call to the data layer:

    Public Function Load(ByVal d As DomainObject) As DomainObject
        Dim dao As Object
        For Each dao In _SupportedDAOs
            If dao.SupportsLoadType = d.GetType.ToString() Then
                Dim hydratedObject As DomainObject
                Try
                    dao.ConnectString = _Connection
                    hydratedObject = dao.Load(d.Key)
 
                    If (Not IsNothing(hydratedObject)) Then
                        hydratedObject.ObjectDomainMGR = Me
                    End If
                Catch ex As Exception
                    Throw ex
                Finally
                    Try
                        If Not IsNothing(dao.DataReader) Then
                            dao.DataReader.Close()
                        End If
                    Catch ex As Exception
                        Trace.Write(ex.Message)
                    End Try
                End Try
 
 
                Return hydratedObject
            End If
        Next
 
        Throw New Exception("Load not supported for this type[" + d.GetType.ToString() + "]")
    End Function

Notice how load takes any domain object as a parameter.  The code then walks through a list of supported Database Access Objects (DAOs) to find the appropriate data access functionality for the object given.  This means that any object in our system can be loaded just by passing it into a single function, making it easy for new developers to be able to use our pattern without having to understand the workings of the DAO classes or the domain manager.  All they need to know is to call DomainManager.Load()!

 

Events

Raising events is a great way to "bubble up" notifications from a class to a caller.  In order to create an event, simply use the Event keyword as seen in class Test below.  To raise an event simply use the RaiseEvent command and the event will be passed to the caller of the object.  In the example below you can see a very simple example where the class TestCaller has a variable of class Test declared with the WithEvents keyword.  This keyword must be used in the declaration in order to capture events.

The sub PostSaveLogic() is declared as a handler of the TestSaved event.  That is to say that whenever Test.Save() is called, the code within PostSaveLogic is called.  An example of where this could be useful is in sending email notifications to users.  The test object certainly doesn't need to know how to send email, email functionality should be encapsulated into an email interface which can be called when an event that requires email notification occurs.

Public Class Test
    Event TestSaved()
 
    Public Sub save()
        RaiseEvent TestSaved()
    End Sub
End Class
 
Public Class TestCaller
    Private WithEvents t As New Test
 
    Public Sub SaveTest()
        t.save()
    End Sub
 
    Public Sub PostSaveLogic() Handles t.TestSaved
 
    End Sub
End Class

 

Declaration Options

Here is a list of the most commonly-used declaration options in .NET with brief descriptions of each:

  • Private: The Private keyword defines a variable or method as accessible only by code within the context of where the declaration occurred; outside code is not permitted access.
  • Public: The Public keyword declares a property or method as accessible by anyone within the calling application or within the class itself.
  • Friend: The Friend keyword defines a property or method as accessible by members within the class it is declared in.
  • Protected: The Protected keyword defines a property or method as accessible only by members of its class or by members of an inheriting class.
  • Default: A Default property is a single property of a class that can be set as the default. This allows developers that use your class to work more easily with your default property because they do not need to make a direct reference to the property. Default properties cannot be initialized as Shared or Private and all must be accepted at least on argument or parameter. Default properties do not promote good code readability, so use this option sparingly.
  • Overloads:The Overloads property allows a function to be described using deferent combinations of parameters. Each combination is considered a signature, thereby uniquely defining an instance of the method being defined. You can define a function with multiple signatures without using the keyword Overloads, but if you use the Overloads keyword in one, you must use it in all of the function's Overloaded signatures.
  • Shared:The Shared keyword is used in an inherited or base class to define a property or method as being shared among all instances of a given class. If multiple instances of a class with shared properties or methods are loaded, the shared properties or methods will provide the same data across each instance of the class. When one class alters the value for a shared property, all instances of that class will reflect the change. Shared properties of all instances of the class point to the same memory location.
  • Overridable:The Overridable keyword is used when defining a property or method of an inherited class, as overridable by the inheriting class.
  • Overides: The Overides keyword allows the inheriting class to disregard the property or method of the inherited class and implements its own code.
  • NotOverridable: The NotOverridable keyword explicitly declares a property or method as not overridable by an inheriting class, and all properties are "not overridable" by default. The only real advantage to using this keyword is to make your code more readable.
  • MustOverride: The MustOverride keyword forces the inheriting class to implement its own code for the property or method.
  • Shadows: The Shadows keyword works like the Overloads keyword except that with shadows we do not have to follow rules such as implementing the same signature. The Shadows keyword does not require the consent (override ability) of the inherited class to replace the property or method's implementation code. A method does not have to be defined as overridable for the Shadows keyword to work.



Comments

TrackBack said:

# February 14, 2005 5:56 PM

TrackBack said:

# February 14, 2005 5:58 PM

TrackBack said:

# February 22, 2005 11:17 AM

TrackBack said:

# February 24, 2005 6:30 AM

TrackBack said:

# March 7, 2005 9:25 PM

TrackBack said:

# March 16, 2005 6:12 AM

Raymond Lewallen said:

For you new programmers or programmers new to OOP, this
article will briefly explain the 4 major...
# April 7, 2005 6:55 AM

Ramon Leon said:

You have a ways to go before you understand OO, objects are not about data, not about exposing data with entities, not about implementing interfaces. None of your sample code is OO, they're classes but they aren't OO. OO is a mode of thinking. OO is about hiding all data, exposing only behavior. OO is about reversing the flow of a program, procedural programs pull data through the system, object oriented programs push data through the system. Real object oriented code tends to create systems full of tiny object, with few methods containing more than about 7 to 10 lines of code, systems where objects work together without gutting each other for data by having all their private data exposed with properties. How does your DAO get all the data from the domain object?
Procedural answer:
aParam = domainObject.Value;
aParam = domainObject.Value;
aParam = domainObject.Value;
aParam = domainObject.Value;

Object Oriented answer:
domainObject.ExportTo(daObject);

Where export to takes a paramater of a generic interface that dao happens to implement. This let's the object keep it's data private from the rest of the system, while still allowing it to dump it's data when necessary. Why? Because later you'll need to do this...

domainObject.ExportTo(xmlBuilder);
domainObject.ExportTo(webPageBuilder);
domainObject.ExportTo(webServiceBuilder);
domainObject.ExportTo(fileSerializer);

all implementing the same export interface, all allowing the domain object to keep it's data private. That is what encasulation means. Encapsulation isn't about just hiding implementation, it's about hiding everything, especially data.

Good objects also don't load themselves, nor are their properties set one at a time via properties, that is very bad code. Learn to use constructors, objects should be created with a one shot call to a constructor that contains "ALL" required data, preventing any possible chance of ever having an invalid object. A good rule of thumb is to have 3 basic constructors, a no arg constructor to allow serialization, a minimum required fields constructor(you'll use this most), and an all fields constructor(dao's will use this).

Your sample code from your domain manager pattern, violates so many rules of good OO design that it's quite obvious you haven't been reading the right books. There are few books that are any good, and almost all the experts agree on what those are, try reading some of the bibles of the industry like Analysis Patterns, Refactoring, Streamlined Object Modelling, Domain Driven Design, Patterns of Enterprise Application Architecture, Smalltalk Best Practice patterns, Design Patterns, or Object Oriented Software Construction. Any one of these would help your style dramatically and assist you in the journey from your current heavily procedural style, to a true OO style.

# June 24, 2005 4:13 PM

Eric Wise said:

Ramon,

I find the tone of your comment slightly offensive but I'll respond anyway.

1) I'd love to see where you got your "rule" about a few methods containg about 7-10 lines of code. I'd also like to know how developers who come after you deal with the massive amounts of spaghetti code such a rule would generate?

2) The constructor method of loading data is not appropriate in every situation. I do agree that in some situations it is more elegant to pass all the object data to the constructor but I prefer not to have a constructor with "all fields" parameters because if I have a routine that loads a subset of data (not the minimum) then I have to worry about ORDER of parameter values in the constructor (logical error), and I have to pass nothing/null in for a bunch of values I don't care about which I simply find annoying.

3) I don't see anything in my post that would prevent you from having an "ExportTo" in the base class so I'm going to assume you're just nitpicking.

4) Yes, the examples given are not an "object oriented purist's" view. However I'm trying in this post (marked level 000 for a reason) just to show the very basic concept of how inheriting an object, etc works. The discussion about when, where, and how to set up a "pure" OO system is far beyond the scope of this post.

5) On the domain manager pattern:
This pattern was built and designed to accomplish some very rapid development for DATA-CENTRIC systems. We don't all live in a perfect world where business needs allow us to spend time developing this elegant wonderful framework you speak of. The domain manager pattern:
a) works

b) has codesmith templates that wire up 90%+ of the code

c) uses a small bit of entry level OO concepts, which is necessary since the organizations I've used it in have developers who have ZERO OO experience and to dump advanced concepts on them would not only frustrate them but cause delays in coding that would be unacceptable to the customer.
# June 25, 2005 7:48 AM

Eric Wise said:

Now that I've cooled down a bit and talked this over with a fellow codebetter blogger I have a few questions for you if you check this thread again:

1) You claim that objects don't load themselves and then claim that using constructors is the way to go. Small contradiction there.

2) In addition, objects not loading themselves, what do you think the singleton pattern does?

3) "The number one way to tell an OO programmer who get's it, from one who doesn't, is that those who don't, make accessors for all their data, and those who do, keep all the data private and only expose functions publicly." An accessor (property) is nothing more than a public method.
# June 25, 2005 8:52 PM

Raymond Lewallen said:

For you new programmers or programmers new to OOP, this article will briefly explain the 4 major...
# July 19, 2005 6:48 AM

Raymond Lewallen said:

This article briefly explains the 4 major principles that make a language object-oriented: Encapsulation, Data Abstraction, Polymorphism and Inheritence.
# July 25, 2005 10:11 AM

Raymond Lewallen said:

Author: <a href="/blogs/raymond.lewallen">Raymond Lewallen</a>
<br>
This article briefly explains the 4 major principles that make a language object-oriented: Encapsulation, Data Abstraction, Polymorphism and Inheritence.
# July 25, 2005 10:13 AM

metalscreamer said:

Just one small thing. The friend access modifier is used to allow access to any object within the same assembly. There is also the protected freind that modifier that allows methods to be accessed by any object in the same assebly or any inheriting object outside of the assembly.

Raymond I'm sorry but the title is OO level 000. Also any good OO designer knows that these "rules" you speak of need to be broken at times.
# August 9, 2005 12:48 PM

Fritz Schenk said:

I find your article useful. However, your exposition of OOP is heavily drawn from the MS world where OOP is done quite differently from the basic tenets of OOP. In you polymorphism paragraphs you might quote from Smalltalk "Polymorphism is a unique characteristic of object-oriented programming whereby different objects respond to the same message with there own unique behavior".

This fits to interface based programming OK.

# December 19, 2006 1:49 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!