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:
|
Related Reading
VB.NET Core Classes in a Nutshell |
Consider the following class, named Person:
Public Class Person
Public age As Integer
Public name As String
Public female As Boolean
End Class
The 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 female indicates
the gender. If female is True, then the Person is female, otherwise the Person
is male.
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
the 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 Person class,
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
the Person class described above. Let's modify our Person class to have a
determining constructor.
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
The new Person class has a constructor that accepts two arguments: a String and
a Boolean. Now, there is no way the client can instantiate a Person object
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!)
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.
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 Person
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
Copyright © 2009 O'Reilly Media, Inc.