Creating Collection Classes


You will need to create one final class before you move on to the user interface: the RegionMgr class. The RegionMgr class manages a collection of your Region objects. Collection classes have changed significantly between VB 6 and VB .NET. It is still possible to use the collection object that was provided with VB 6 in the same manner under .NET. However, the .NET Framework also comes with some great collection classes from which you can inherit. These classes are more efficient in handling collections because they are built with specific purposes in mind. The two collection classes that you will probably use most often will be the CollectionBase and DictionaryBase classes. Each of these classes is for a specific purpose, and both have advantages and disadvantages. These collection classes (as well as others) are in the System.Collections namespace.

The CollectionBase Class

The CollectionBase class stores values that can be retrieved by index only. This class handles moving through a collection using a For..Next loop and is extremely efficient. However, you cannot use a key to find the item you want. It does, however, already implement an enumerator, so you can use a For..Each..Next loop to retrieve all the values without having to code it. Listing 3-14 contains an example of an object that inherits from the CollectionBase class.

Listing 3-14: A CollectionBase Example

start example
 Public Class CollectionTest     Inherits System.Collections.CollectionBase     Public Sub Add(ByVal obj As SomeObject)         list.Add(obj)     End Sub     Public Function Item(ByVal Index As Integer) As SomeObject         Return CType(list.Item(Index), SomeObject)     End Function     Public Sub Remove(ByVal Index As Integer)         list.RemoveAt(Index)     End Sub End Class 
end example

As you can see from this example, the object itself is simple. The list object is a protected object that is internal to the CollectionBase. It is roughly analogous to the collection object in VB 6. The Add, Item, and Remove methods are the only methods you need to implement in your code (and technically you do not need to implement the Remove method). You need to implement the Add method because the base class does not know what type of object you are going to add. You need the Item method because the base class does not know what type of item it is returning. You need the Remove method because there are two ways to remove an item from the CollectionBase—Remove and RemoveAt. Also, each one is handled slightly differently. So, if you do not implement a Remove method, the only publicly available method to remove an object is the RemoveAt method.

The CollectionBase class has one major advantage to the other collection classes: It implements the IList interface. This may seem like a small thing, but you can bind any class that implements the IList interface to list boxes and combo boxes. Instead of looping through the entire collection, all you have to do is write the following lines of code:

 ComboBox1.DataSource = objMgr ComboBox1.DisplayMember = "FirstName" 

All you have to do is assign the object to the DataSource. If the object overrides the ToString method, the combo box will be filled with whatever value is returned. If the ToString method is not overridden (of even if it is and you want to specify another property), simply assign the property you want to display to the DisplayMember property of the list control as you did in the second line of Listing 3-14.

The DictionaryBase Class

The DictionaryBase class provides a dictionary object. It is almost identical to the Scripting.Dictionary object in VBScript—with a couple of great improvements. The scripting object in VBScript was a key/value collection that could only contain simple data types. The .NET version of the DictionaryBase also holds a key/value pair, but the key can be anything and the value can be any type of object. In the typical VB 6 collection, if you tried to add a key that was numerical, you received an error unless you appended a string onto the end of the key, typically a K. With the DictionaryBase, the key can be numerical or character based; it is your choice. However, you cannot use the For..Next loop to return all of the values. You must use the For..Each..Next loop to retrieve these values. Unfortunately, the collection does not return the object that the collection holds. It returns a DictionaryEntry object, which adds a little overhead to the process, but not much. You will see an example of this as you start coding your forms.

Caution

None of the collections guarantee that enumerating through the collection will return the items in the order you placed them into the collection. This may affect how you retrieve the information from a collection.

The DictionaryBase offers an extremely efficient means of retrieving an object based on the key that is passed to it. Because you are going to be using keys on all of your tables, you will be implementing the DictionaryBase class in your collection classes.

Note

If you need to implement a collection that can be searched by index or by key (which you cannot do with the DictionaryBase class), I recommend using the Collection object and implementing it as you would in VB 6. However, this is not nearly as efficient as .NET's built-in collection classes, and it does not implement the IList interface.

The RegionMgr Class

Now that you have a little background on collection classes, it is time to implement your RegionMgr collection class. In the same code module as the Region class, add the code in Listing 3-15 (add this outside of the Region class or you will get a syntax error).

Listing 3-15: The RegionMgr Collection Class

start example
 Public Class RegionMgr     Inherits System.Collections.DictionaryBase     Public Sub Add(ByVal obj As Region)         dictionary.Add(obj.RegionID, obj)     End Sub     Public Function Item(ByVal Key As Object) As Region         Return CType(dictionary.Item(Key), Region)     End Function     Public Sub Remove(ByVal Key As Object)         dictionary.Remove(Key)     End Sub End Class 
end example

This code is almost identical to the CollectionBase class that I showed you in Listing 3-14, except that the DictionaryBase exposes a dictionary object to any inheriting classes. Also, as mentioned earlier, you are using a key to retrieve the objects and setting a key in the Add method.

Note

A common mistake is to set the object and key in the same order as in VB 6. This is the reverse of what it was in VB 6. Now, you pass in the key first and then the object that is being added to the collection. Hence, the term key/value pair.

Now you need to add a method to load your collection of Region objects. Add the code in Listing 3-16 to the RegionMgr class.

Listing 3-16: The Load Method of the RegionMgr Class

start example
 Private Sub Load()      Dim objIRegion As IRegion      Dim dRow As DataRow      Dim ds As DataSet      objIRegion = CType(Activator.GetObject(GetType(IRegion), _      AppConstants.REMOTEOBJECTS & "RegionDC.rem"), IRegion)      ds = objIRegion.LoadProxy()      objIRegion = Nothing      For Each dRow In ds.Tables(0).Rows           Dim objRegion As New Region(Convert.ToInt32(dRow.Item("RegionID")))           With objRegion                .Loading = True                .RegionDescription = _                     Convert.ToString(dRow.Item("RegionDescription")).Trim                .Loading = False           End With           Me.Add(objRegion)      Next      ds = Nothing End Sub 
end example

This code simply retrieves the dataset from the remote object, loops through it, and adds it to the collection. Notice that this method is private because if someone is going to instantiate this object, they are going to do it for the purpose of loading it. So why have them make two calls? Notice that you have not implemented a constructor yet; you will do this in the "Implementing the Singleton Pattern" section.

The next small block of code refreshes the collection from the database. At this point you might ask, "Why don't I just make the load method public and then I don't need a refresh routine?" This is a matter of convention. You do not want developers who are writing the interface to have to call the load routine to refresh the object. That could cause more problems than it would solve with developers calling the load routine right after instantiating the object. However, it is just a matter of personal preference. Add the refresh routine as follows:

 Public Sub Refresh()      dictionary.Clear()      Load() End Sub 

The dictionary.Clear method is a method that is internal to the dictionary object that clears all of the objects contained within it.

Understanding Design Patterns

Design patterns are relatively new to VB developers because their full potential could not be explored in VB 6. Most design patterns really do require a full object-oriented language as opposed to an object-based language. Some design patterns could be implemented in VB 6, but it was often more trouble than it was worth. Volumes of material have been written about patterns and anti-patterns, and this book does not go into them.

Note

An excellent introduction and reference on the subject is Design Patterns: Elements of Reusable Object-Oriented Software Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley, 1995). I recommended this book earlier to learn about objects, but it also discusses objects in terms of how to use them in design patterns.

A design pattern is a way of seeing how something works in a generic sense and being able to develop a framework for solving the problem. A popular example of a pattern—and one you are using all the time even though you might not realize it—is called the Observer/Observed pattern. This pattern says that one object can observe another object and that the object being observed can tell things about itself to the observer. Where do you think this pattern might be in use? Whenever a control raises an event to a form, the Observer pattern is in use. This is because the form observes the control, and the control tells the form about itself.

I am going to introduce my favorite pattern and perhaps the most useful pattern I have ever used—the Singleton pattern. You will recall from Chapter 2, "Building an N-Tier Application," that you set your remote objects to the Singleton mode as opposed to the SingleCall mode. In doing this, I said that there would only ever be one instance of the object created. That is exactly what the Singleton pattern offers you—and the implementation is simple. For the purposes of this application (and most applications I have worked on), you do not want more than one Region collection in memory at one time. After all, what do you gain by having multiple Region collections in memory except used memory? You may find the need to have multiple collections of certain objects in memory, such as a collection of order details because you may have multiple orders open at any given time. In a case such as that, the Singleton pattern would be more harmful than helpful. Using this pattern needs a lot of careful thought. It gives you a great deal in that you only ever have to go to the database once (unless you want to refresh the collection), but it does take some thought beforehand.

So, how does the Singleton pattern work? The principle is that the object instantiates itself and there is only ever one object during the course of application execution. It requires that there be no publicly available constructor that can be called.

Implementing the Singleton Pattern

To implement the Singleton pattern, add the code in Listing 3-17 to the RegionMgr class after the Inherits statement.

Listing 3-17: The RegionMgr Implementation of the Singleton Pattern

start example
 Private Shared mobjRegionMgr As RegionMgr Public Shared Function GetInstance() As RegionMgr      If mobjRegionMgr Is Nothing Then           mobjRegionMgr = New RegionMgr()      End If      Return mobjRegionMgr End Function Protected Sub New()      Load() End Sub 
end example

Here you have done a number of things to make this work correctly. The first thing you did was declare an object of type RegionMgr as a shared module-level variable. This means that only one instance of this object will ever exist. By declaring it as private, you only allow access to it through the GetInstance method. Because the GetInstance method is shared, you do not have to do the following to instantiate the class:

 Public Sub GetRegionMgr()      Dim objRegionMgr As New RegionMgr() End Sub 

And, in fact, doing so will give you a syntax error because the constructor is now protected. It is only available to your object or any objects that inherit from your object.

The GetInstance method takes care of instantiating a new object if one does not already exist. And it always returns the reference to mobjRegionMgr regardless of whether it has to instantiate the object. The constructor takes care of calling the Load method when the object is first instantiated.

Note

This causes a slight delay when getting a reference to this object the first time, but it saves having this delay every time you need a list of Regions. The delay only happens once here (and optionally at other times if you call the Refresh method).

The following example shows how you will get a reference to the RegionMgr object if you need it:

 Public Sub GetRegionMgr()      Dim objRegionMgr As RegionMgr      objRegionMgr = objRegionMgr.GetInstance End Sub 

You now have a reference to the RegionMgr object, but you never once had to call the constructor.

Note

One area this can help in is having the object registered with various other objects that can all respond to the events generated by the Singleton object. Although this might not be considered a best practice, it does save the interface developer from having to pass objects all over the place because the object that implements the Singleton pattern acts as a global object now.

Whew! You are now done with your business objects for the moment, and it is time to create the user interface and test your creation.




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