In our last tutorial we learned how Interfaces can help us separate the Implementation of code from the Interface of the code. If you remember, we defined an Interface to manage books, and this interface was implemented by two collections. By using a common interface for both classes we could use one form to manipulate and maintain these two collection classes. The Presentation layer was in fact capable of maintaining any class that implemented our defined Interface.
However, the more observant among you would have noticed that we literally destroyed the DRY Principle in this exercise. We have swathes of code, repeated in both the VBBookCollection class and the NonFictionBookCollection class. This is inherently bad, a jail-able offence, for reasons we’ve discussed before. If we wanted to change the workings of these classes we’d have to go to two places. If we had 100 of these classes we soon have a maintenance nightmare on our hands. In the days of Visual Basic 6 this is what we were stuck with, and believe me it sucked. However the powers that be at Microsoft introduced something we’d been crying out for years when they launched VB.NET – Inheritance.
Inheritance is a key principle of what is known as Object Oriented Programming. With the introduction of classes, programmers and designers had a new way to articulate programming concepts. Rather than routines and variables, we had objects. Objects that we could describe verbally and diagrammatically. The Objects would typically have a single responsibility in an application, and they would interact with other Objects. People got very carried away for a while and some of the design documentation was tougher than rocket science to understand. I’m not joking. Things got pretty messy for a while. People produced so much documentation about these objects there wasn’t much time to code anymore. Things have improved substantially in recent years and it took bright sparks to come up with the Agile Manifesto to essentially say, lets use our common sense here. We’re here to produce working software, that’s what people are paying us for. We’re not here to produce documentation that describes what the software would do if we had time to write it. I have heavily paraphrased what they said but that’s the learnings I took from it. One of the authors of the manifesto, the great Bob Martin, expanded on the DRY Principle with his Single Responsibility Principle. This principle states that each object in a system should have a single well articulated responsibility, and that responsibility should be completely encapsulated (or contained) within a class. Obviously in our previous we obliterated that principle too; we have repeated code in a number of places. For us to best comply with the Single Responsibility Principle, we need to utilise what is known as Inheritance.
Open up the Collection Class project again. In this application there are two classes doing essentially the same thing. For example, the VBBookCollection and NonFictionBookCollection both have an Add method, and the code looks identical. It is identical! Similarly, they both have a Sort technique that uses the exact same algorithm, repeated in two places. How, using a class, can we stop this?
What we need is a class which contains all our methods and properties. Using Inheritance we can Inherit these in our VBBoolCollection and NonFictionBookCollection classes as so it feels, from a consumer’s point of view, that these methods and properties are part of the two collection classes.
So how do we write this Base Collection Class?
Go to your project and add a new Class called BookCollectionBase.
Paste the following code into the class.
Public MustInherit Class BookCollectionBase Implements IBookCollection Private colBookNames = New Collection Private colISBN = New Collection Public Sub Add(ByVal item As String, ByVal isbn As String) Implements IBookCollection.Add AddItem(item, isbn) End Sub Public Sub Remove(ByVal isbn As String) Implements IBookCollection.Remove colBookNames.Remove(isbn) colISBN.Remove(isbn) End Sub Public Function Contains(ByVal isbn As String) As Boolean Implements IBookCollection.Contains Return colBookNames.Contains(isbn) End Function Public Function Count() As Integer Implements IBookCollection.Count Return colBookNames.Count End Function Public Function Item(ByVal isbn As String) As String Implements IBookCollection.Item Return colBookNames.Item(isbn) End Function Public Function Item(ByVal index As Integer) As String Implements IBookCollection.Item Return colBookNames.Item(index) End Function Public Function Key(ByVal index As Integer) As String Implements IBookCollection.Key Return colISBN.Item(index) End Function ' Add an item at its correct position. Protected Sub AddItem(ByVal item As String, ByVal key As String) Dim i As Integer ' See where the item belongs. Iterate through the entire list For i = 1 To colBookNames.Count If colBookNames.Item(i) >= item Then Exit For End If Next i ' Insert the item. If i > colBookNames.Count Then ' Add at the end. colBookNames.Add(item, key) colISBN.Add(key, key) Else ' Add at the right position. colBookNames.Add(item, key, i) colISBN.Add(key, key) End If End Sub End Class
This code is the exact same code in both of the BookCollection classes. There are a couple of minor differences.
We have changed the AddItem method from
Private Sub AddItem(ByVal item As String, ByVal key As String)
Protected Sub AddItem(ByVal item As String, ByVal key As String)
Can you remember the discussion on variable scope which defined when and from where a variable could be accessed? We embellished on this in the tutorial on Modules with Function and Subroutine scope? A Private method is only accessible within that class. A Protected Method is accessible within that class and any class which inherits that class. We call these derived classes, and both the VBCollection class and NonFictionCollection class will inherit this class, but they will also need to access this method. Hence we needed to change the accessibility to Protected.
In addition we have changed the class definition to say
Public MustInherit Class BookCollectionBase
This signals to Visual Basic that this is a base class that must be inherited. We can’t create an instance of this class in isolation, only one of the derived classes. You’ll often see this referred to as an Abstract class (as opposed to a Concrete class). It needs to be inherited.
In your VBCollectionClass, replace the code with this
Public Class VBBookCollection Inherits BookCollectionBase Public Sub New() AddItem("Microsoft Visual Basic 2010 Step By Step", "0735626693") AddItem("Sams Teach Yourself Visual Basic 2012 in 24 Hours, Complete Starter Kit", "0672336294") AddItem("Distributed Applications with Microsoft Visual Basic 6.0 McSd Training Kit : For Exam 70-175", "0735608334") AddItem("Microsoft® ASP.NET Programming with Microsoft Visual Basic® .NET Version 2003 Step By Step", "0735619344") AddItem("Visual Basic 6 Design Patterns", "0201702657") AddItem("Excel VBA Programming For Dummies", "0470503696 ") AddItem("Learn to Program with Visual Basic", "1902745000") AddItem("Visual Basic 6 Complete", "0782124690") End Sub End Class
In our NonFictionBookCollection add the following code:
Public Class NonFictionBookCollection Inherits BookCollectionBase Public Sub New() AddItem("The 4 Hour Workweek", "0091929113") AddItem("The Lean Startup", "0670921602") AddItem("Purple Cow: Transform Your Business by Being Remarkable", "014101640X") AddItem("Rework", "0091929784") AddItem("Code Complete", "0735619670") AddItem("Pragmatic Programmer", "020161622X") AddItem("Leaving Microsoft to Change the World", "006112107X") End Sub End Class
Both of these concrete classes inherit the BaseCollection class. To a consumer of these classes ie our Presentation layer, all Public methods and Properties in the base class will appear to be part of the derived class. In fact, to all intents and purposes, once a class has inherited a class, both the derived class and the base class are the same class.
Run your code. Alter the Sub Main to use both types of collection class. The code works as before, but we are now obeying the Single Responsibility Principle. No code is repeated, we have one place to manage our base method calls and one place to manage the derived method calls. Look at how little code we need in these derived classes! That should be a signal that we are working in a far more efficient, clean manner.
Our application can now be diagrammatically described as follows
It may look complicated, and for our purposes of managing two classes it probably is. However we now have a robust architecture in place for adding more collections. With minimal code we can add new book collections to this codebase. We can also enhance the inner workings of one base class which will affect all the derived classes. We have developed something that is maintainable. We have developed a true, professional Visual Basic application.