Once the VB6 upgrade has been completed, the time is right for a Visual Basic .NET application advancement, that is, the process of adding new features to your upgraded VB application. Chapters 17, 18, 19, and 20 are provided to give you ideas about the kinds of advancements you can make to your applications: VB.NET object-oriented features,VB.NET application layering, VB.NET code refactoring, VB.NET data serialization, and more. Remember that by studying Visual Basic .NET and the .NET Framework in detail, you will discover even more technologies and features that you can add to your application to increase their value to your business for years to come.
Application advancement is the process of adding new features, or improving the existing features, of an application. It is moving beyond functional equivalence after upgrading an application.
Any advancement plan will require a new architecture, design and implementation. Architecture topics will impact the overall system and what it depends on, along with relationships to other systems. The design advancements will organize the code and help you determine which portions of your system can be reused and which must be recreated anew. Implementation advancements will improve the performance, readability, and maintainability of the code.
Visual Basic .NET is a fully object-oriented language that supports features like the following:
Layers represent a build-up of functionality. The higher layers are dependant on the lower layers to supply the required information and methods. A single layer represents a logical group of functionality that is contained in an application tier that usually corresponds to functionality that is bound to a physical resource such as a database server. In this way, application tiers are composed of one or more logical layers that build the functionality provided by the tier.
It is important to keep in mind that lower layers should almost never rely on higher layers for operation. A group of layers usually present a coherent whole. For example, you may be familiar with the concept of dividing an application among various tiers that are composed of groups of layers, such as the presentation tier (UI), the business logic tier (functionality), the data tier (storage and retrieval), and so on. Each tier represents a coherent unit of the application and serves a single purpose, such as displaying information to the user or representing business logic.
Careful layer design is important. Unnecessary layers can harm performance.
An advantage to layers is that substitution is possible. This means that you can pull out a layer and provide a different layer that adheres to the interface but provides a completely different level of functionality. This is very helpful for unit tests; it is also helpful for applications that may require different business logic or database access but no other changes.
A disadvantage of layers is that cascading interface changes may lead to more work. Adding a field to a screen or page may require a change not only in the layers of the presentation tier, but it may also require a change in the business and data tiers.
Refactoring is an iterative process whose goal is to transform existing applications according to modern software engineering quality criteria. When refactoring is applied to an application, the characteristics of the application are improved in terms of clarity, maintainability, and redundancy reduction, amongst others.
Refactoring may involve small changes, such as changing variable or subroutine names, or it may require large changes, such as consolidating functionality into a single component or breaking apart an existing component into multiple components that more logically isolate functionality.
The best approach to refactoring is to attempt to do so in small steps. This will help to ensure that the refactoring process does not introduce defects. Smaller refactoring processes, such as identifying poor variable names, may take only minutes. Larger refactoring processes, such as identifying functionality that can be consolidated (or separated) can take hours, days, or even weeks. However, even for extensive or intensive refactoring, it is still best to proceed in small steps.
The following motivations are among the most common:
It implies the transformation of existing applications according to software design patterns. A pattern is based on an abstraction of a group of similar software designs that is further refined and at the end constitutes a model that contains the most relevant characteristics of the original applications. Patterns are obtained with a logical inductive process that has been applied to real-life application designs. Software design patterns are also enriched with good design principles and standards that will allow the user to obtain a better product when the pattern is applied.
Patterns can assist users with creating large-scale applications and with resolving recurring design problems with proven and highly effective guidance. When an application is refactored according to design patterns, the resulting structure of the application can be understood as a derivation of the pattern(s) applied; this reduces the future development and maintenance risks.
Because of some limitations in Visual Basic 6.0, many programmers have been forced to use the different functions that are available in the Windows API. For operations such as registry manipulation, finding window names, or finding the names of special folders, Visual Basic 6.0 developers were forced to use the Windows API because there was no other way to carry out these tasks.
In Microsoft Visual Basic .NET, all the barriers that held back Visual Basic 6.0 developers and forced them to resort to the Windows API are completely gone. For every function that accomplished a specific task in the Windows API, there is a way to achieve the same result in .NET using intrinsic functions.
You can still use Windows API calls by means of using interop services, but your .NET applications are usually be better off using the functions included in the .NET Framework. The amount of work required to get your .NET application working with the same API calls from Visual Basic 6.0 may be more than what you would expect. Calling these APIs in .NET is not as simple as it is in Visual Basic 6.0, and there may be some additional overhead making the necessary corrections so that changes in data types from Visual Basic 6.0 will adhere to the type specifications of the APIs. Furthermore, by using API calls in your .NET application, you are immediately limiting your application to be specific to a particular version of the Windows operating system.
Even though there is no harm in using Windows APIs in Visual Basic .NET, your code may be more portable and compatible if you replace your API calls by using .NET intrinsic functions. The end result may be the same, but if you ever need to move part of your code toward another .NET language or operating environment you know that you will not be held back by adhering to .NET functions instead of using the Windows API.
Built-in registry manipulation from within Visual Basic 6.0 has been somewhat limited to the following four functions: SaveSetting, GetSetting, GetAllSettings, and DeleteSettings. Although these functions allow registry editing, they are limited in the subtrees they can access: HKEY_CURRENT_USER\Software\VB and VBA. Because of this limitation, many programmers use the Windows API to perform registry modification tasks from within Visual Basic 6.0.
Two new classes that offer the same registry manipulation functionality as the Windows Registry API have been added to the .NET Framework. These classes are the Registry class and the RegistryKey class; they are located in the Microsoft.Win32 namespace. The Registry class provides the set of standard base keys found in the registry. Using it, you can define the root key on which you will be working.
After the base key is defined, you can use the RegistryKey methods to perform the necessary registry actions, like adding, deleting or changing subkey values. There are equivalent .NET registry method calls for the most common operations performed using the Windows registry API functions. For example, if you used the RegCreateKey, RegOpenKeyEx, or RegDeleteKey API calls, you can now use the RegistryKey class methods CreateSubKey, OpenSubKey, or DeleteSubKey respectively. These .NET methods offer the same functionality as their API counterparts and are easier to use.
By using the Registry and RegistryKey classes, you will be able to manipulate the registry from within Visual Basic .NET. The end result will be exactly the same as when using the Windows API registry functions from within Visual Basic 6.0. In most cases, the necessary code to perform these operations will be smaller and more understandable than the API counterpart.
Note that Registry access has been simplified in Visual Studio 2005 with the addition of the My.Computer.Registry object and the addition of two new methods to the Microsoft.Win32.Registry object that My.Computer.Registry points to. These objects provide access to the registry on the local computer. The two new methods are GetValue() and SetValue(); they allow you to read from and write to arbitrary keys in the registry without having to first navigate the registry tree.
When coding applications that do not have or need access to a database, there is frequently a need to store data used in the program for later retrieval. Before .NET, any attempt to serialize data required custom code. For example, if the programmer needed to serialize the information from a class or data structure, a procedure could have been written that would write each instance of the class/structure to an XML document. Programming this code usually required a great deal of work and testing to make sure that the implemented serialization worked correctly. Fortunately, carrying out this task with .NET does not require writing, testing, or debugging custom code because it has been carefully planned and is very straightforward to implement.
Using .NET, there are different serialization schemes that can be used in your application. Each technique has its own positive and negative aspects; choosing the correct technique depends on the requirements that you may have for storing your data.
The first technique, shallow serialization, uses classes found in the System.Xml.Serialization namespace, specifically the XmlSerializer class. Using this class, you can easily serialize and deserialize data in your code to XML format. You must first create an XmlSerializer instance and pass to the constructor the type of the class that you want to serialize. You can then create an object of type FileStream to store the data and then call the Serialize method of the XmlSerializer instance that was previously defined.
On the other hand, binary serialization allows the storage of both private and public properties. This technique is found in the System.Runtime.Serialization namespace and requires a bit more coding than the XMLSerializer method. The class to be serialized using binary serialization must have the <Serializable()> attribute declared. This communicates to the serialization object that it can go ahead and try to serialize the object. Then you need to first declare a BinaryFormatter object to store the data and use a FileStream object and the Serialize method of the BinaryFormatter class to store this information to a file.
In the System.Collections namespace, you can find a series of classes and interfaces that define a series of objects that can be used to implement sets of closely related objects in your classes. This namespace includes collections that can be used immediately, such as ArrayList, Stack,Hashtable, and Queue, but it also includes interfaces such as IEnumerable that allow you to create your own collection classes.
By having classes implement collections instead of dealing with groups of instances by means of arrays, all the caveats that using arrays usually bring, such as (but not limited to) run-time errors, invalid indexes, or array length tracking, are no longer concerns. By implementing collections, you are also following good object-oriented practice in the sense that you can easily employ other concepts such as inheritance and polymorphism in the future. Following this approach will also aid you in avoiding errors in the future by re-utilizing stable code.