What Else Do We Need to Know?

Chapter 10 - Componentization
byJohn Kauffman, Fabio Claudio Ferracchiatiet al.?
Wrox Press ?2002

While we've certainly made some inroads into the subject of using componentization in your ASP.NET-based web applications, we've really only scratched the surface. In the last section of this chapter, we'll look at some of the issues that will surely arise as you begin to strike out on your own.

Stored Procedures Versus Class Libraries

As you know, a stored procedure is a set of T-SQL statements stored within the database in compiled form, allowing any number of programs to reuse the functionality it offers. Stored procedures can be helpful in controlling access to data, and using them often results in superior overall database and application performance. Compare this with a class library, which is a collection of classes stored in a compiled form. They too can be used in different programs, and can be helpful in controlling access to data and application logic. Which then should we use, and when?

The principal difference between these two lies in the scope within which each is used. Because stored procedures are stored and executed on the SQL Server, they tend to be used only by those classes that directly interact with the database. A class library, on the other hand, may be used by any class that can 'see' it. In practice, it's quite common to see components that manage database access use stored procedures, allowing the developer to separate data operations from business operations. We'll have more to say on this subject in the later sub section called Complexity.

Compatibility

It's a sure thing that at some stage after you've written a class library you'll need to update it - to add functionality, or to improve its performance, or to fix that bug you never quite got to grips with first time around. When you do so, you need to be careful to ensure that the changes you make have no adverse effects on the applications that have been happily using your library so far.

Suppose, for example, that you have a function that expects three strings in its parameter list:

     Public Function GetSummary(Msg As String, Val As String, Amt As String)         As String       Return Msg & ": " & Val & " = " & Amt     End Function 

If, in some ASP.NET page, you have a statement like:

     ThisTextBox.Text = GetSummary("Notice", "Balance Due", BalDue.ToString("c")) 

Then clearly, this will assign "Notice: Balance Due = $150.25" to the Text property of ThisTextBox. Now, suppose that for some reason you replace the GetSummary() function with a new version, as follows:

     Public Function GetSummary(Msg as String, Val as String, Amt as Decimal) _         As String       Return Msg & ": " & Val & " = " & Amt.ToString("c")     End Function 

If you don't remember to update the web page to:

    ThisTextBox.Text = GetSummary("Notice", "Balance Due", BalDue) 

Your users will likely see an ugly error complaining about the type of BalDue, instead of the expected page. This occurs because you didn't recompile the ASP.NET web page (where you would have caught this error); you only recompiled the class library.

Typically, it won't be you that suffers the ill effects of your changes. Rather, it will be the developers who reused your class library. Unless you have good documentation, they won't know that you changed the function, and they won't expect to have to update their pages. Here are three rules of thumb for writing good components that avoid compatibility problems:

  • As far as possible, consider all non-private interfaces as 'set in stone'.

    That is, you should design your classes as completely as possible before releasing them for use. It's acceptable to expose new interfaces at a later date, but don't remove or change existing ones.

  • If you must change the interface of a public method, don't replace it.

    Rather, you should use overloading. For example, if we just added the new version of GetSummary() and changed the previous version as follows:

     Public Function GetSummary(Msg As String, Val As String, Amt As Decimal)     As String   Return Msg & " : " & Val & " = " & Amt.ToString("c") End Function Public Function GetSummary(Msg as String, Val as String, Amt as String)     As String   Return GetSummary(Msg, Val, Convert.ToDecimal(Amt)) End Function 

    The developers who didn't update their pages wouldn't get errors, and those who did update would be able to use your library as well. Keep in mind, though, that some users may be using implicit data type conversions, so they may find themselves calling the wrong version of the method unexpectedly.

  • Document and distribute documentation about your changes on a before-and-after basis. Ideally, when you add a new method to a class, it's best to state how it worked before and how it will work after your change. If you replace a method with a new-but-similarly-named method, show both the old interface and the new one. This will make it easier for other developers to update their pages and programs that use your class library, because they will be better able to understand the changes they have to make.

Complexity

Although the use of classes and class libraries produces simpler code in the end, it can be a challenge to get the design right first time. From a functional perspective, the approach we took with NwtLibrary is perhaps not the best solution, because we've tried to do a lot of things in one place. Our ProductInfo class not only handles the task of getting data into and out of the database, but it also validates business rules and provides elements for use in the user interface. As it stands, if we needed to change any of these elements, we would have to redistribute the whole component after compiling (and testing) it.

An alternative design would be to have multiple specialized classes. We could have one class that deals only with the loading and updating of data in the database; another for building the objects needed for presentation in our web pages; and yet another for checking and assuring the integrity of the data according to Northwind's business rules. These three layers - database access, business rules, and presentation - are commonly used boundaries in good componentized software. If we focus our database access class purely on getting data to and from the database, then our business rules and presentation rules can interface with that. If we subsequently need to change the database hosting our data, we can just focus on changing and updating that class, without affecting the other two.

That's not to say that we should aim for a goal of having only one or two methods in each class, resulting in dozens of components supporting one design goal. This greatly increases the complexity for developers who need to figure out which class they should use, and increases the chances that a bad change will go undetected. It's a balancing act, but ideally you should try to keep the number of classes (and class libraries) as low as possible, without sacrificing too much flexibility.

A very important issue related to complexity is that class libraries act like multipliers - if a 'bad' change is made to a class library, it can break several applications simultaneously, multiplying the impact of that change by the number of affected pages. An apparently innocent change can result in an avalanche of problems that you have to dig through in order to find the cause. The best way to combat this effect is to write a set of pages that you know will test your classes thoroughly, and to adhere to a thorough testing procedure before releasing a new version.

Documentation

A cost of using class libraries that's often overlooked is that their documentation generally needs to be more detailed than one might expect. This is largely because many developers want to understand how your classes work (particularly the data flow through them) in order to use them properly. You will also need to provide a clear set of statements about what the component does in normal situations, and how it reacts to exceptions. Also consider providing documentation of the attributes of each exposed method, in addition to traditional documentation. More often than not, the developer will open the object browser to see a brief description of what a method does and how to use it, before they crack open 'real' documentation.

Having this level of documentation also benefits you when, six months or more after you've released a library, you need to make a change. Rather than trying to remember how it worked, you can start by reviewing your own documentation. When making a change to a class, remember our rules of thumb: overload rather than replace, show the before-and-after state of the component, and be careful of issues relating to automatic type conversion.



Beginning ASP. NET 2.0 and Databases
Beginning ASP.NET 2.0 and Databases (Wrox Beginning Guides)
ISBN: 0471781347
EAN: 2147483647
Year: 2004
Pages: 263

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