VB.NET OOP Part 4: Determining Constructorsby Budi Kurniawan, co-author of VB.NET Core Classes in a Nutshell
We all know that constructors can be used to initialize values. But, did you know that you can use determining constructors to "shape" the objects of a class? In this OOP series article, Budi Kurniawan shows what a determining constructor is and why it is sometimes imperative to have one. Note that in this article, "client" refers to the application that uses your class.
In VB, if you don't write a constructor explicitly in your class, the compiler creates a no-argument constructor for that class. So, basically, an object-oriented (OO) programmer can live without worrying too much about constructors. Many OO programmers concentrate on writing methods and properties in their classes. Many don't realize that constructors can help ensure that the client only instantiates objects based on certain criteria. I call such constructors determining constructors. Basically, a determining constructor is a normal constructor that accepts certain arguments. Having these arguments will ensure that the class instances behave in the way the class designer has intended. In addition, determining constructors can be used for the following purposes:
- To ensure that a class is used properly, thus eliminating the possibility of having certain errors.
- To prevent the class instances from functioning if the client cannot supply the correct serial number for your software. In other words, to protect your intellectual property.
- To prevent an object from being instantiated if the client does not pass the correct arguments.
Making Sure Your Class Is Properly Used
Consider the following class, named
Public Class Person Public age As Integer Public name As String Public female As Boolean End Class
Person class encapsulates details about a person. The
age field stores
the person's age, the
name field is for the person's name, and
the gender. If
True, then the
Person is female, otherwise the
For example, the following code instantiates a
Person object and then sets its
fields, and retrieves them back:
Dim bestFriend As New Person() bestFriend.age = 25 bestFriend.female = True System.Console.WriteLine("My best friend's name is " & bestFriend.name) System.Console.WriteLine(bestFriend.name & " is a " & _ IIf(bestFriend.female, "woman", "man")) System.Console.WriteLine(IIf(bestFriend.female, "She", _ "He") & " is " & bestFriend.age & " years old")
The result when the program is run is as follows:
My best friend's name is is a woman She is 25 years old
At a glance, you know that the result is not as expected, don't you? Know what
causes it? Yes, the programmer forgot to assign a value to the name field of
Person object after the object was instantiated. But that's fine, the
error is easily caught. However, consider another scenario. This time, here is
the code that instantiates a
Person object, sets the field values, and retrieves
the values back:
Dim bestFriend As New Person() bestFriend.age = 25 bestFriend.name = "Alex Simpson" System.Console.WriteLine("My best friend's name is " _ & bestFriend.name) System.Console.WriteLine(bestFriend.name & " is a " & _ IIf(bestFriend.female, "woman", "man")) System.Console.WriteLine(IIf(bestFriend.female, "She", _ "He") & " is " & bestFriend.age & " years old")
The result is:
My best friend's name is Alex Simpson Alex Simpson is a man He is 25 years old
Looks good, doesn't it? But, does it produce the correct result? Is Alex Simpson
really a man? Notice that we never set the
female value of the
and by default its value is
False. Is this because the programmer knew that the
default value is
False, or is it because he/she forgot to set it? We don't know.
This could lead to a serious bug.
Using a determining constructor, you can avoid the two potential problems with
Person class described above. Let's modify our
Person class to have a
Public Class Person Public age As Integer Public name As String Public female As Boolean Public Sub New(ByVal name As String, ByVal female As Boolean) Me.name = name Me.female = female End Sub End Class
Person class has a constructor that accepts two arguments: a
Boolean. Now, there is no way the client can instantiate a
without passing the person's name and sex. This means, we have eliminated the
possibility of having a
Person object without a name and without indicating the
right gender for that person.
Here is how the programmer will use the
Person class now:
Dim bestFriend As New Person("Alex Simpson", True) bestFriend.age = 25 System.Console.WriteLine("My best friend's name is " & _ bestFriend.name) System.Console.WriteLine(bestFriend.name & " is a " & _ IIf(bestFriend.female, "woman", "man")) System.Console.WriteLine(IIf(bestFriend.female, "She", "He") & _ " is " & bestFriend.age & " years old")
And, here is the result of executing the code:
My best friend's name is Alex Simpson Alex Simpson is a woman She is 25 years old (So, Alex Simpson is a woman!)
Protecting Your Intellectual Property
You have just discovered a quick way to generate very big prime numbers. In computer science and mathematics, prime numbers are a hot topic. It normally takes ages for computers to calculate the next big prime number, but your algorithm has made it very simple and fast. You write this algorithm as a method in your class and it is of course a top secret. This is your hard work and you have spent years on research on this algorithm; therefore, you want to make sure only those who pay can use your class. You do this by requesting the buyer to hand in their public key of the RSA asynchronous cryptography method for you to include in the class. You then re-compile the class and send it to the buyer. The class can only work if the client passes their private key to decrypt a secret field that has been encrypted using their public key. You can use a determining constructor that accepts the private key. Because normally people don't distribute their private key (otherwise the key won't be private anymore), you can rest assured that no one is using your class without paying for it.
Warning: since source files are compiled into intermediate language (IL) code in the .NET Framework, there is always a possibility to decompile it and view the source code.
Forcing the Client to Pass Correct Arguments
In the previous example with the
Person class, the class uses a determining
constructor to make sure that a
Person object always has a name and the correct
gender. The determining constructor is as follows.
Public Sub New(ByVal name As String, ByVal female As Boolean) Me.name = name Me.female = female End Sub
However, this constructor only makes sure that the client passes certain
arguments. It does not guarantee that the argument has a correct value. For
example, the client can still create a
Person object by passing an empty string,
or a null, or a string containing spaces only, as the first argument.
Dim bestFriend As New Person(Nothing, True)
Even though it is legal to have a
Person object that has a null name, it does
not make sense. You should prevent this from happening.
You can do more with determining constructors to enforce certain rules. For example, you can throw an exception from your constructor if you are not happy with the argument the client passes to your class.
The following code is the modified
Person class that cannot be instantiated if
the name argument to the constructor is null:
Public Class Person Public age As Integer Public name As String Public female As Boolean Public Sub New(ByVal name As String, ByVal female As Boolean) If name Is Nothing Then Throw New Exception("Error! Person does not have a name.") Else Me.name = name Me.female = female End If End Sub End Class
If the client passes a null, an
Exception is thrown and there won't be a
whose name is null.
You can do a lot with determining constructors. For example, if you require that the client passes a valid birthday to the person class, you can make sure that the date entered is not later than today.
Budi Kurniawan is a senior J2EE architect and author.
Return to ONDotnet.com