Restructuring Visual Basic code



.NET Programming Standards and Naming Conventions. You can greatly improve the quality of existing code by restructuring it.

By restructuring we mean arranging the code in logical modules and classes, grouping related functions and data together, and using scope rules to achieve better legibility and maintainability.

Goals
• Add modularity
• Go object-oriented
• Encapsulate data access
• Limit visibility by scoping

Restructuring the code
Spaghetti may be delicious, but not when you find it in your code. It's often necessary to rewrite code to reuse it, to understand how it works, to modify it, or to throw away unnecessary code and write new functionality. Let's learn a few simple ways to restructure code to achieve better quality. PA This sign denotes that Project Analyzer contains a feature to help with a particular task. The use of this utility is not required for restructuring, but it will make it easier. Project Analyzer helps you to understand how your code works. It also reviews source code for some issues mentioned in this article.

Add modularity
Think of a module as a group of related procedures. What you'd want to do is to find groups of procedures that belong together, and put them all to a single module. The goal is to minimize the number of dependencies between modules. When a module calls procedures from another module, these modules are coupled. Coupling also occurs when a procedure uses variables, constants, declare statements or other elements of another module. You want to keep coupling to a minimum, to make it easier to read the code, and also to make the module a better candidate for reuse in another program.

So how do you find a group of related procedures? That's a tough question. If you know your code (and you should), you probably already have an idea which procedures are related. Now, find out which procedures call these procedures, and where the calls eventually lead to. A call tree is your primary source of calls/called by information. Another thing to consider are the variables, constants and other elements required. PA

There's no single solution to the module division problem. It's more like a trade-off between minimal coupling and optimal module size. You don't want to group all of your procedures into a single giant module, even though that minimizes coupling. It's better to get an idea of a good module size. You'd want modules to contain, say, max 30 procedures (that's not a recommendation, just an example). If the module becomes larger, you might want to consider splitting it into two - even if it increases coupling.

Go object-oriented
Once you've found a group of related procedures, it's time to decide whether you really want a build a new module or rather a new class. You'd want to use a class if the procedures work intensively on a certain set of data. A module is better if the procedures don't work on the same data, or if you can live by passing the data as procedure parameters.

Object-oriented programming may be a way of life, but it's also a practical means to improve the quality of code. We're not going into the details when to use or not to use object-oriented programming. When you suspect you should go object-oriented, you should try it.

Convert a module to a class
Even though classes and modules are different, going from a module to a class may actually be quite simple, provided that the code is suitable.

Before creating your class you should verify that most, if not all, data access is already limited to happen inside the module. By this we mean that no other parts of your program will read or write the variables declared in the module (that will become your class). You ensure this by declaring all variables private. Verify that your project compiles. If it doesn't, you're accessing the variables from outside. You'll need to provide accessor methods to set or get the data. In VB, you build Public (or Friend) properties to get/set the values of private variables.

If your code doesn't compile when your variables are Private, turn them back to Public for now. Write the properties once you have your class ready. You also need to get rid of public constant and type declarations. Classes don't define public constants or data types. Either change them to Private, or move them to a module that you keep for project-level declarations.

Public Enums are good to leave in the class. That's true if they're required by public methods or properties of your class, but not used in other ways outside of the class. If you can, turn your Enums to Private.

Steps to create a class
1.Create a new empty class
2.Copy all the code "as is" from the module to the new class
3.Remove the module from the project - but don't delete it yet!
4.Compile & fix repeatedly until your code works. You'll need to fix all the places that reference your old module to references to your new class.

The last step should take the most time. You'll need to create object variables to hold instances of your new class, create the instances with the New keyword, and turn procedure calls to object.method syntax. If you're working in VB classic (6.0), try not to declare your new object variables like this:

Dim obj As New Class
This is a slow way to program. Everywhere you use the variable obj, VB will check if it's already been instantiated or not. This slows down your program. Instead, declare the variable without the New keyword, and instantiate it separately in a sensible place.

Dim obj As Class
Set obj = New Class

You may find out that it did not make sense to go object-oriented. If this happens, just revert back to your original module. You did take a backup copy, didn't you?

Encapsulate data access
Classes shouldn't usually allow variable read/write from outside of the class. Instead, you should provide accessor properties that will set and get the variable values.Encapsulated variables should be declared Private. The use of inheritance in .NET might also justify a Protected variable. The accessor properties are usually Public or Friend depending on your project.

What's so good about encapsulated data? First of all, you can make data read-only (or write-only). So how do you make data read-only (or write-only)? In VB classic, set the Property Let/Set part Private, and you have property that only gets its values from inside the class. In VB.NET you can use the special ReadOnly keyword. Similarly, if you want to create a write-only property, create a Private Property Get, or use VB.NET's WriteOnly keyword.

It's advisable to check the data in the Property Set block before you store it. For example, you can make sure that a numeric value is within acceptable bounds. So what if you need to change the way you store your data? With properties, no problem. You just rewrite the properties to access the new data storage. No need to touch the code that accesses the properties.

Encapsulation example
Here you can see one example of variable encapsulation. Here the data is read/write, and the acceptable values are from 0 to 130.

Private mAge As Integer ' Encapsulated variable
Accessor property to retrieve the value
'Public Property Get Age() As Integer
Age = mAge
End Property

'Accessor property to set the value, verifying that the value is in pre-defined limits
Public Property Let Age(ByVal newAge As Integer)
If newAge >= 0 And newAge <= 130 Then
mAge = newAge
Else

'Age not in limits, raise a customer error
Err.Raise 12345, "MyClass.Age [Let]", "Invalid age"
End If
End Property

Limit visibility by scoping
Not all code should be accessible from everywhere in your project(s). You should hide the details and only give a limited number of points of access to any module or class..

Scoping rules are simple. There are only 2:
1.Use as limiting scope as possible. A limited scope enforces modular programming and encapsulation. You access code only via well-defined interfaces and get rid of unpleasant suprises caused by someone accessing code that's not written to be accessed from outside of its context. Use Private whenever possible.

2.Always declare a scope. VB's default scoping rules are somewhat complicated. If you don't give a scope the declaration can be anything from Public to Private, depending on where you wrote it. When you always write a scope you can't misdefine or misunderstand it. So instead of Dim write Private, instead of Sub write Public or Private Sub, instead of Class write Public/Friend/Private Class. The use of Dim is particularly worth noting. You should use Dim only inside procedures, but never outside of them.

Scope - Availability- Use
Public - Everywhere- Use when you need to give access to the whole project, or expose the code to other projects.

Friend - In the declaring project - Friend is required when building projects that expose an interface to other projects. Use Friend to keep other projects from accessing the code.

Private - In the declaring class/module - Use for implementation procedures, all data, and enum/user-defined type definitions required only inside the class/module.

Protected (.NET only) - In the declaring class and its descendants - Use in base classes, but only when required. Exposed to other projects via inheritance.

Protected Friend (.NET only) - In the declaring project, and in descendant classes - Use in base classes, but only when required. Exposed to other projects via inheritance.

PA This sign denotes that Project Analyzer contains a feature to help with a particular task. The use of this utility is not required for restructuring, but it will make it easier. Project Analyzer helps you to understand how your code works. It also reviews source code for some issues mentioned in this article.

.NET Programming Standards and Naming Conventions Common .NET Naming Conventions
These are the industry-accepted standard naming conventions for J#, C# and VB.NET programs. For additional information, please see the MSDN help documentation and FX Cop. While individual naming conventions at organizations may vary (Microsoft only suggests conventions for public and protected items), the list below is quickly becoming the de-facto standard in the industry.Please note the absence of Hungarian Notation except in visual controls. These naming standards should find their way into all of your .NET development, including ASP.NET Web applications and .NET Windows Forms applications.)

Namespaces - Pascal Case, no underscores. Use CompanyName.TechnologyName as root. If you don't have a company, use your domain name or your own initials. Note that any acronyms of three or more letters should be pascal case (Xml instead of XML) instead of all caps. - AppliedIS.TimeCard.BusinessRules

IrritatedVowel.Controllers
PeteBrown.DotNetTraining.InheritanceDemo
PeteBrown.DotNetTraining.Xml
Assemblies - If the assembly contains a single name space, or has an entire self-contained root namespace, name the assembly the same name as the namespace. - AppliedIS.TimeCard.BusinessRules.dll

Classes and Structs - Pascal Case, no underscores or leading "C" or "cls". Classes may begin with an "I" only if the letter following the I is not capitalized, otherwise it looks like an Interface. Classes should not have the same name as the namespace in which they reside. Any acronyms of three or more letters should be pascal case, not all caps.Try to avoid abbreviations, and try to always use nouns.l

IWidget
Enumerations - Follow class naming conventions. Do not add "Enum" to the end of the enumeration name. If the enumeration represents a set of bitwise flags, end the name with a plural.

SearchOptions (bitwise flags)
AcceptRejectRule (normal enum)

Functions and Subs - Pascal Case, no underscores except in the event handlers.Try to avoid abbreviations. Many programmers have a nasty habit of overly abbreviating everything. This should be discouraged.

Functions and subs must differ by more than case to be usable from case-insensitive languages like Visual Basic .NET

VB: Public Sub DoSomething(...)
C#: public void DoSomething(...)

Properties and Public * Member Variables - Pascal Case, no underscores. Try to avoid abbreviations. Members must differ by more than case to be usable from case-insensitive languages like Visual Basic .NET.

VB: Public Property RecordID As Integer
C#: public int RecordID

Parameters - Camel Case.Try to avoid abbreviations. Parameters must differ by more than case to be usable from case-insensitive languages like Visual Basic .NET.VB: ByRef recordID As Integer

C#: ref int recordID

Procedure-Level Variables - Camel Case
VB: Dim recordID As Integer
C#: int recordID ;

Class-Level Private and Protected Variables
- Camel Case with Leading Underscore. In VB.NET, always indicate "Protected" or "Private", do not use "Dim". Use of "m_" is discouraged, as is use of a variable name that differs from the property by only case, especially with protected variables as that violates compliance, and will make your life a pain if you program in VB.NET, as you would have to name your members something different from the accessor/mutator properties.

Of all the items here, the leading underscore is really the only controversial one. I personally prefer it over straight underscore-less camel case for my private variables so that I don't have to qualify variable names with "this." to distinguish from parameters in constructors or elsewhere where I likely will have a naming collision. With VB.NET's case insensitivity, this is even more important as your accessor properties will usually have the same name as your private member variables except for the underscore.

As far as m_ goes, it is really just about aesthetics. I (and many others) find m_ ugly, as it looks like there is a hole in the variable name. It's almost offensive. I used to use it in VB6 all the time, but that was only because variables could not have a leading underscore. I couldn't be happier to see it go away.

Microsoft recommends against the m_ (and the straight _) even though they did both in their code. Also, prefixing with a straight "m" is right out. Of course, since they code mainly in C#, they can have private members that differ only in case from the properties. VB folks have to do something else. Rather than try and come up with language-by-language special cases, I recommend the leading underscore for all languages that will support it.

If I want my class to be fully CLS-compliant, I could leave off the prefix on any C# protected member variables. In practice, however, I never worry about this as I keep all potentially protected member variables private, and supply protected accessors and mutators instead.

VB: Private _recordID As Integer
C#: private int _recordID ;

Controls on Forms - Modified Hungarian Using .NET Class Names, or a common "control" or "ui" prefix for all controls. This keeps the controls together in intellisense and makes UI programming much easier.

In recent projects (the past couple years), I have taken to a single prefix for all my UI controls. I typically use "ux" (I used to use "ui", but it wasn't set apart well in intellisense). "ux" comes from my usual design abbreviations where it means "User eXperience". I have found this to be extremely helpful in that I get the desired grouping in the intellisense even better than if I use "txt", "lbl" etc. It also allows you to change combo boxes to text boxes etc. without having to change the names.

txtUserID, lblHeader, lstChoices, btnSubmit
or "ux" prefix (preferred!)
uxUserIDText, uxHeaderLabel, uxChoiceList, uxSubmitButton

* Public class-level variables are universally frowned upon. It is considered to be a much better practice to use property procedures (accessors and mutators) to provide read and/or write access to a private member variable. If you must expose a member variable to other classes using "Public", follow the property naming conventions, but don't complain if your guilty conscience keeps you up at night ;-).

Please don't copy and paste these conventions on your own site. Feel free instead to link directly to this page. That way you get the ability to automatically get updates when I make them, as well as get that warm fuzzy you get by not copying someone else's work. Schools and accredited educational institutions can paste these conventions on their own sites if and only if they include a direct link to this page an an attribution for the source of the information from "Pete Brown's irritatedVowel.com". Thank you for respecting my wishes on this.

You Can find the standards for publicly exposed classes/properties etc at MSDN . If you want to run a tool to validate your code for public standards and required practices, download FXCop.

Let's face it: when you work in SQL Server you already know what is a table, what is a view and what is a stored proc. All the GUI tools put them in separate buckets. So instead of going by type, name your stored procedures by function and the object they work on, not with some standard prefix like "P" (or even worse, "sp_" - something that is contra-indicated by Microsoft for performance reasons). When you name your procedures use "by" for the sort order, and "for" for criteria and start them off with the logical object name that the procedure works with. I also see no real reason for prefixes like "usp_" as all they do is add more characters to the name. Keep it simple and informative.

"Select" Stored Procedure Name Examples
CustomerGetSingleForID
retrieves a customer by ID
CustomerGetListAllByName

Retrieves a list of customers ordered by name. I usually use "lists" to populate drop-downs, pick lists etc, but not for updating. For that reason, lists often only have a few fields. OrderGetListForCustomerIDByDate retrieves a list of orders for the customer, sorted by date)

.

ICT Nieuws.

Nuttige items


WA40 - New Media