Consolidating the User-Centric Classes


Let's start consolidating your code by creating another class module called BusinessBase in the NorthwindUC project. Because you are taking duplicate code and adding it to this class, this will be an easy and painless process (well, mostly painless). Copy the code for each of the methods and properties that have no differences in Table 8-1 to the new BusinessBase class. When you are done, your BusinessBase class should look like the class in Listing 8-1.

Listing 8-1: The BusinessBase Class

start example
 Option Explicit On Option Strict On Public Class BusinessBase     Private mblnDirty As Boolean = False     Public Loading As Boolean     Private WithEvents mobjRules As BrokenRules     Public Event ObjectChanged(ByVal sender As Object, _     ByVal e As ChangedEventArgs)     Public Event BrokenRule(ByVal IsBroken As Boolean)     Public ReadOnly Property IsDirty() As Boolean         Get             Return mblnDirty         End Get     End Property     Public ReadOnly Property IsValid() As Boolean        Get            If mobjRules.Count > 0 Then                Return False            Else                Return True            End If        End Get     End Property     Private Sub mobjRules_RuleBroken(ByVal IsBroken As Boolean) _     Handles mobjRules.RuleBroken         RaiseEvent BrokenRule(IsBroken)     End Sub End Class 
end example

Before doing anything else, you need to make one decision—do you want to allow a developer to instantiate this class? In this case the answer is easy—no, you do not. The reason is that this is a helper class, and it cannot operate on its own. So before going any further, add the MustInherit keyword between the words Public and Class in the class declaration.

Now you have to change some of the declarations so that your variable and method scopes are OK. For every method or property you need to access in your subclassed object, you need to change scope declarations from Private to Protected. In this case, you need to access only two variables: the mblnDirty variable and the mobjRules variable. So, change the scope from Private to Protected for these two variables. Next, alter just the Region class for right now by having it inherit from the BusinessBase class:

 Public Class Region     Inherits BusinessBase 

This immediately causes several things to be underlined as errors. Do not worry. If you look at the Task List, you will notice that the only errors it shows are that different variables, methods, and events conflict with each other. Simply delete them. Every piece of code you moved to the BusinessBase class needs to be deleted from the Region class because it inherits from the BusinessBase class. Once you are done with this, you will find out that you still have a problem. The error you will see in the Task List is the following:

 Derived classes cannot raise base class events. 

Uh-oh. How do you fix this? The answer is you cannot, but you can work around it. You can get around it by creating a method in the base class that you can call and have it raise the event for you. In the base class, create a method called CallChangedEvent and code it as follows:

 Protected Sub CallChangedEvent(ByVal sender As Object, _ ByVal e As ChangedEventArgs)      RaiseEvent ObjectChanged(sender, e) End Sub 

Next, in the Save method of the Region object (because that is where the errors occurred), change the two RaiseEvent statements to look like the following:

 If intID = 0 Then      CallChangedEvent(Me, New _           ChangedEventArgs(ChangedEventArgs.eChange.Added)) Else      CallChangedEvent(Me, New _           ChangedEventArgs(ChangedEventArgs.eChange.Updated)) End If 

This simple change eliminates your problem, so you are good to go.

Now, perform all of these steps for the Territory class (with the exception of the intID, which is not needed in the Territory class). Your base business class now consists of 27 lines of code that you never need to write again. And you have already, with just these two classes, reduced the amount of code by 27 lines. Although this does not seem like a lot, consider a project with 100 classes (which is still a fairly small amount of classes)—this would then have reduced the number of lines of code by 2,700 lines! That is a big difference. But you can do better still.

The GetBusinessRules method is redundant. Although it does call a different remote object, that is just a value that you append onto the end of the REMOTEOBJECTS path. If you look at the GetBusinessRules method in the Region class, you will see the code in Listing 8-2.

Listing 8-2: The GetBusinessRules Method of the Region Class

start example
 Public Function GetBusinessRules() As BusinessErrors         Dim objIRegion As IRegion         Dim objBusRules As BusinessErrors         objIRegion = CType(Activator.GetObject(GetType(IRegion), _         AppConstants.REMOTEOBJECTS & LISTENER), IRegion)         objBusRules = objIRegion.GetBusinessRules         objIRegion = Nothing         Return objBusRules     End Function 
end example

You have used the IRegion interface to get a reference to the GetBusinessRules function of the remote objects. You definitely want to consolidate this call because it operates on an object and is not really a part of an object. To do this, you need to make a change to your Region and Territory interfaces. Go to the Interfaces code module in the NorthwindShared project. Listing 8-3 shows the Region and Territory interfaces.

Listing 8-3: The Region and Territory Interfaces

start example
 Public Interface ITerritory         Function LoadProxy() As DataSet         Function LoadRecord(ByVal strID As String) As structTerritory         Function Save(ByVal sTerritory As structTerritory) _         As BusinessErrors         Sub Delete(ByVal strID As String)         Function GetBusinessRules() As BusinessErrors     End Interface     Public Interface IRegion         Function LoadProxy() As DataSet         Function LoadRecord(ByVal intID As Integer) As structRegion         Function Save(ByVal sRegion As structRegion, ByRef intID As Integer) _         As BusinessErrors         Sub Delete(ByVal intID As Integer)         Function GetBusinessRules() As BusinessErrors     End Interface 
end example

The similarities are immediately obvious upon comparing the two interfaces. Both the LoadProxy and GetBusinessRules methods are identical. To consolidate these methods, add a new Interface called IBaseInterface and add the method signatures to it as shown in Listing 8-4.

Listing 8-4: IBaseInterface

start example
 Public Interface IBaseInterface      Function LoadProxy() As DataSet      Function GetBusinessRules() As BusinessErrors End Interface 
end example

Next, alter the IRegion and ITerritory interfaces so they both inherit from IBaseInterface. Delete the LoadProxy and GetBusinessRules methods from each of these interfaces. At this point you should not have any errors. Now, what does this have to do with consolidating the GetBusinessRules method in your objects? Now that the GetBusinessRules method is included in a base class, your call on the GetBusinessRules method can be performed on the IBaseInterface interface instead of on the IRegion or ITerritory interfaces. This allows you to create a generic routine that can make this call as long as the interface inherits from the IBaseInterface interface.

First, add the following two Imports statements to the top of the BusinessBase code module:

 Imports NorthwindTraders.NorthwindShared.Interfaces Imports NorthwindTraders.NorthwindShared.Errors 

Move the GetBusinessRules method to the BusinessBase class and alter the code so that it is identical to the code in Listing 8-5.

Listing 8-5: The GetBusinessRules Method

start example
 Public Function GetBusinessRules() As BusinessErrors      Dim objInterface As IBaseInterface      Dim objBusRules As BusinessErrors      objInterface = CType(Activator.GetObject(GetType(IBaseInterface), _      AppConstants.REMOTEOBJECTS & LISTENER), IBaseInterface)      objBusRules = objInterface.GetBusinessRules      objInterface = Nothing      Return objBusRules End Function 
end example

Now you still have one problem here—the Activator.GetObject line, in particular the Listener constant. You do not have a listener constant in the base class and you do not have a value to put into it at this point anyway. You need to make this dynamic, but how? The answer lies in the constructor that you will create for this class. Add the following private variable and constructor to the BusinessBase class as shown in the following code:

 Private mstrListener As String Public Sub New(ByVal RemotePath As String)      mstrListener = RemotePath End Sub 

And alter the Activator.GetObject line to read as follows:

 objInterface = CType(Activator.GetObject(GetType(IBaseInterface), _ AppConstants.REMOTEOBJECTS & mstrListener), IBaseInterface) 

This causes several errors also, so let's go back and edit the constructors for the Region object so they read as follows:

 Public Sub New()      MyBase.New(LISTENER)      mobjRules = New BrokenRules()      mobjRules.BrokenRule("Region Description", True) End Sub Public Sub New(ByVal intID As Integer)      MyBase.New(LISTENER)      mobjRules = New BrokenRules()      mintRegionID = intID End Sub 

Notice that both methods now have a new first line that reads as follows:

 MyBase.New(LISTENER) 

Make the same change for the Territory object so that the first line of its constructors now reads as follows:

 MyBase.New(LISTENER) 

The last two changes are fairly simple: Delete the GetBusinessRules method from the Region and Territory classes and move the mobjRules = New BrokenRules() statement from the constructors in the Region and Territory classes to the constructor in the BusinessBase class. At this point you should be able to run the application with no problems at all. Your count of reusable code lines is now up to 41. And with that, you are done consolidating the user-centric classes.

Note

The purpose of doing this is not just so that you reuse code. Although that is a laudable goal itself, it also allows you to place code that does one thing in one place. But more importantly than that, it gives your application a true, overall architecture. Every type of object in your application will now behave in substantially the same way. This makes maintenance a lot easier, and it makes adding new functionality to the entire application easier.




Building Client/Server Applications with VB. NET(c) An Example-Driven Approach
Building Client/Server Applications Under VB .NET: An Example-Driven Approach
ISBN: 1590590708
EAN: 2147483647
Year: 2005
Pages: 148
Authors: Jeff Levinson

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net