Content by Category
.NET 1.x
.NET 2.0
.NET 3.0
.NET 3.5
.NET 4.0
.NET Assemblies
.NET Framework
.NET Getting Started
Accessibility
ADO.NET
Advertorials
Agile Development
AJAX
Architecture
ASP.NET
ASP.NET MVC
ASP.NET WebForms
Azure
B2B (Business Integration)
Bing
BizTalk
Book Excerpts
Build and Deploy
C#
C++
ClickOnce
Cloud Computing
Code Contracts
CODE on the Road!
COM+
Community
Conferences
Continuous Integration
Crystal Reports
CSLA.NET
CSS
Data
Design Patterns
Development Process
Display Technologies
Distributed Computing
DotNetNuke
DSL
Dynamic Programming
Editorials
Enterprise Services ("COM+")
Entity Framework
Events
Expression Blend
F#
Fox to Fox
Frameworks
Functional Programming
Git
Graphics
Internet Explorer 8.0
Interviews
iPhone
Iron Ruby
Java
Java Script
jQuery
LINQ
Linux
Mac OS X
MDX
Microsoft Application Blocks
Microsoft Business Rules Framework
Microsoft Dynamics
Microsoft Expression
Microsoft Office
Mobile Development
Mobile PC
Mono
MsBuild
Network
NHibernate
Object Oriented Development
Odata
Open Source
Opinion
Opinions
Oracle
ORM
Other Languages
Parallel Programming
Patterns
Podcasts
Post Mortem
PowerPoint
Print/Output
Prism
Product News
Product Reviews
Project Management
Python
Q&A
Rails
Rake
Reporting Services
REST
RIA Services
Ruby
Ruby on Rails
Search
Security
Services
SharePoint
Silverlight
SOA
Social Networks
Software & Law
Software Business
Source Control
Speech-Enabled Applications
SQL Server
SQL Server 2000
SQL Server 2005
SQL Server 2008
SQL Server CE/AnyWhere/Mobile/Compact
SSIS
Subversion
Sync Framework
Tablet PC
TDD
Team System
Techniques
Testing and Quality Control
Tips
UI Design
UML
User Groups
VB Script
VB.NET
Version Control
VFP and .NET
VFP and SQL Server
Virtual Earth
Vista
Visual Basic
Visual Basic 6 (and older)
Visual FoxPro
Visual Studio .NET
Visual Studio 2005
Visual Studio 2008
Visual Studio 2010
Visual Studio Tools for Office
VSX
WCF
Web Development (general)
Web Services
WF
Whitepapers
Windows 7
Windows Azure
Windows Live
Windows Server
Windows Vista
WinForms
Workflow
WPF
XAML
XML
XNA
XSLT



Virtual Brown Bag Lunches


 


iPhone iPad Developers Conference

Reader rating:
Click here to read 1 comment about this article.
Article source: CoDe (2009 Mar/Apr)


Article Pages:  1  2 3 - Next >


The Zen of Inversion of Control

On the surface, this article is about the techniques of dependency injection and inversion of control. Underneath, the intent of the words and code samples is to get you to think about the questions of “why” and “when” you might want to use these two closely related techniques, as well a series of similar evolutionary techniques that lead up to the full-blown dependency injection. The initial code samples are admittedly (and deliberately) simple; I do not want the content of the code to obscure the intent of the code.

I’ll use the code samples to take you on a journey. I’ll start with some very simple code and evolve that code through a number of revisions, not unlike the revisions that happen to real programs. The programs will be of limited use to you (unless you live in a particularly impoverished development shop). What is more important is the “color commentary” that accompanies each of these “Zen koan” code samples. [The Zen Master teaches the student by posing “koans” to expand a student’s thinking. A koan is essentially an open-ended riddle or story upon which the student meditates and, in time, achieves wisdom.] With some luck, all of this will lead you to understand the Zen of Inversion of Control.

Genesis

Some time ago, I was looking for a file. I had created the file a year before to support some decisions about employment benefits, made the decisions, and moved on. I had not touched the file for a year. With storage as cheap as it is, I knew that I had kept the file but had only a vague idea of where I might have archived it. I could not remember the name of the file. I knew that it was a spreadsheet, but I was not sure if it had an .xls or .xlsx extension. I decided to write a simple program to look for the file in the most likely places. Once I found it, I could update it and make my decisions about employment benefits for the next year.

The main procedure instantiates a Scanner class that walks the file hierarchy, looking for the file in question. The Scanner class is the protagonist in this story: I’ll show you how this character “grows” as the tale evolves. Listing 1 shows the first version of the Scanner class.

Let me now offer the first round of color commentary. The program does use recursion to walk down the file hierarchy but other than that it is very simple. The functionality of this software, which serves as the first Zen koan to focus our minds, is to:

  • Specify the path to the initial directory.
  • Specify the extension to test for.
  • Specify the path to the log file.
  • Navigate the file hierarchy.
  • Test each file for relevancy.
  • Write the path of each relevant file (along with its containing directory) to the log file.

As this story progresses, I’ll return to this list of functions to see what changes. One important aspect is that the program is totally in control of what it does. If I give you a copy of this program, about the only decisions that you (through the main program) can make are: (1) to run it or not run it, and (2) what computer to run it on. All other aspects of control are lodged in the Scanner class. The consequence of this is that the Scanner class, for a very specific and narrow purpose, is useful, but outside of that area, it is not at all interesting.

Beginnings

As a software developer, my first response is to fix these deficiencies in the Scanner class by adding parameters that specify the initial directory to start the scan in, the extension of the file that I am looking for, and the location of the log file in which to hold the full paths for the files that the scanning class finds. Listing 2 shows the next “Zen koan” to mark this part of the journey.

The functionality is not much different from the first example, but because I have introduced external inputs, I have added some validation of the input values; this might be a “toy” program but professional practices are professional practices. You might not typically think of parameters as inversion of control, but the Scanner class has, by accepting these parameters, given up some aspects of control to an external mechanism. That is, the program is now dependent upon the values of these parameters that I am “injecting” into the program from an external source. The functionality of this altered software is to:

  1. Accept the path to the initial directory (loss of control to an external mechanism).
  2. Accept the extension to test for (loss of control to an external mechanism).
  3. Accept the path to the log file (loss of control to an external mechanism).
  4. Navigate the file hierarchy.
  5. Test each file for relevancy.
  6. Write the path of each relevant file (along with its containing directory) to the log file.

The main program that instantiates and invokes the Scanner class now has much more control over what the Scanner program does. If the main program exposes the options for the Scanner class as program parameters, you and I, as users of this program, get more control. Because the Scanner class is more flexible, it has become more popular (albeit at the cost of losing some of the control over its functionality). I, and others, could use this program to find all sorts of files. (And, yes, you can find utilities that do this much better but these utilities do not illustrate my points nearly as well.)

Delegates

At this point, I am very proud of my Scanner class and show it to my fellow programmers. They are somewhat impressed, but they (being accomplished software developers themselves) suggest all sorts of extensions to the class. Can you add a parameter to specify the minimum and maximum size of the file? How about a pair of parameters to specify the date range when the file was created? Ditto last written? Ditto last accessed? Can you add parameters to specify more than one extension? How about the ability to specify a regular expression for the file name test? And on it goes. I could, of course, write code for each of these possibilities, but it is clear that this would be a never-ending task. Each time that I got one possibility working, someone would suggest yet another possibility. My nice simple Scanner class would be “junked up” in no time.

One of the goals of writing a class is to get it “done”. That is, I want to write the code, write the unit tests, write the documentation, create the deployment logic, and so on, and then put all of this “on the shelf” where I can re-use it. The principle that I’m interested in here is called the “open closed” principle. I want the Scanner class to be open for extension and closed for modification. In other words, I want to create a Scanner class that implements a certain amount of functionality (that is done) that I can extend by specifying parameters or though one of the techniques that I’ll cover in this article. I do not want to have to keep going back to working (and tested) code to make modifications. To achieve this state of near-nirvana is difficult but not impossible. One of the ways to achieve this state of ultimate tranquility is to focus on the relevant essentials and ignore everything else.

If the Scanner class squints its eyes (to abstract out the irrelevant details), it becomes obvious that the only thing that is relevant to the Scanner (for all of the above possible extensions) is whether the individual file “is of interest or not”. The Scanner class really does not care about how this determination is made or even where it is made. All that you have to do is to define a “predicate” function that accepts information about the file and returns a Boolean “true” or “false”. You could make this an overrideable function and create derived child classes that provide the specific implementation of the predicate function or you could create a delegate method signature for the predicate function and pass in an appropriate delegate to handle the relevance test. I’ll show you how to use this second approach. You can see the next “Zen koan” to mark the journey in Listing 3.

The Scanner class walks down the hierarchy, creating instances of the FileInfo class for each file that it finds. It then passes each of these FileInfo class instances to the predicate function. The predicate function determines if the file is of interest or not. I also have made a number of significant changes to the program. Because I was adding a number of different delegates, I decided to create the GUI component in Figure 1.

Click for a larger version of this image.

Figure 1: The GUI for the Scanner class.

This GUI presents three different options (along with modifying parameters) for the predicate function delegate. Since I am using a GUI, I changed the file name capture logic from writing to a specified log file to raising an event that the caller could capture; in the example, the GUI displays the data on a scrollable textbox. Finally, I added the delegate logic to test the relevance of the file.

What have I done here? Take a look at the function list. I’ve altered the functionality of this software to do the following:

  • Accept the path to the initial directory (loss of control to an external mechanism).
  • Accept the extension to test for (loss of control to an external mechanism).
  • Navigate the file hierarchy.
  • Invoke the delegate to test for relevancy (loss of control to an external mechanism).
  • Raise an event for each relevant file (loss of control to an external mechanism).

Two interesting things have happened here. First, I injected the logic for the relevancy test into the Scanner class. Second, I’ve removed the logic from the Scanner class that handles each relevant file; the above code raises an event with the path of each relevant file but has no idea of what will be done, if anything, with that value. It could be written to the console, written to a log file, displayed on a GUI control, and so on. The essence of the Scanner class now is to navigate the file hierarchy; test each file for relevancy, and raise an event for the files that pass the relevancy test. I’ve removed everything else. Paradoxically, the class does less but has become much more useful. Note that I am still passing in the extension. This is really not needed but this is typical of programs that evolve: this is a vestigial parameter that has not quite withered away.

May You Live in Interesting Times

Time has passed. A lot of time has passed. While the vision and implementation of the Scanner class might have seemed close to the sought-after state of perfection, the Scanner class and its supporting software have continued to evolve. (I won’t continue the Zen references from this point forward because now we’re going to deal with the more tangible realities of developing software in a business environment.) In the time that passed since the enhancements I walked you through in the previous section of this article, your development team has modified the Scanner class to respond to requests for:

  • The ability to capture and process the data in the relevant files.
  • The ability to rollup processing after all of the individual files are processed.
  • The ability to output the captured and processed data in various ways.

Vicki Vice President found about this software based on the Scanner class and asks that you build a revenue reporting system for the company’s ancient Rocks-a-Lot order entry system. This is a system that has been in place for almost 30 years and is the system for the division. As each order comes in, the Rocks-a-Lot system creates a file with the details of the order and saves the file within the directory structure where the directory path specifies the company and reporting period. The order fulfillment system scans the directory structure and ships the product. At this point, your inner software developer is screaming that there are an infinite number of opportunities for improving this system. Be patient, Grasshopper. The careers of several vice presidents have crashed upon the shores of the Rocks-a-Lot system in futile efforts to upgrade or replace the system. It has become the dreaded “third rail” of IT systems within the company. Nobody, even the ultra-capable Miss Vicki, wants to take on this particular monster. The only safe thing to do is to build out from the original system.

&

By: Jon Stonecash

Jon Stonecash is a senior consultant at Magenic, a Microsoft Gold Partner consulting company. Jon has worked in software development for much longer than he would like to admit. In that time, Jon has had the opportunity to make most of the serious software development mistakes at least once. He has programmed in over a dozen languages including several different assembly languages, Fortran, COBOL, SNOBOL, classic Visual Basic, VB.NET, and C#. He has survived the structured programming revolution and the object-oriented revolutions. Jon’s software development activities have included the development of operating systems, scientific and engineering applications, and enterprise systems. He has worked in every phase of software development from the initial specification of requirements through to customer support. Along the way, he picked up a BS in Mathematics and an MBA. He still has hopes of finding something that he can be reasonably good at. His long-term interests center about databases and the aspects of the application that handle data access and business logic. He is also interested in the tools and processes that assist the development process. Jon also has an active blog on “Designing Out Loud in the .NET Space” at http://blog.magenic.com/blogs/jons/default.aspx.

jons@magenic.com

Fast Facts

You can build your own simple inversion of control container or use one of the many libraries that provide inversion of control functionality.



Listing 1: The first version of the Scanner class
public class PrimativeScanner
{
   public void Process()
   {
      using (StreamWriter writer = new 
         StreamWriter(@"C:\_Logs\AGenesisXLSFiles.txt", 
         false))
      {
         try
         {
            // hard-coded alert
            ProcessDirectory(@"C:\_Projects", "*.xls", 
            writer);
         }
         catch (Exception e)
         {
            OutputLine(e.ToString(), writer);
            throw;
         }
      }
   }
   private void ProcessDirectory(string directoryPath, 
      string fileExtension, StreamWriter writer)
   {
      bool directoryHasFilesOfInterest = false;

      string[] myFiles = Directory.GetFiles(directoryPath, 
        fileExtension);
      foreach (string myFileName in myFiles)
      {
         if (!directoryHasFilesOfInterest)
         {
            OutputLine("Directory: " + directoryPath, 
              writer);
            directoryHasFilesOfInterest = true;
         }
         OutputLine("    File: " + myFileName, writer);
      }
      string[] mySubDirectories = 
         Directory.GetDirectories(directoryPath);
      foreach (string mySubDir in mySubDirectories)
      {
         // recursion alert
         ProcessDirectory(mySubDir, fileExtension, writer);
      }
   }
   private void OutputLine(string line, StreamWriter writer)
   {
      Console.WriteLine(line);
      writer.WriteLine(line);
   }
}


Article Pages:  1  2 3 - Next Page: 'The Era of Interfaces' >>

Page 1: The Zen of Inversion of Control
Page 2: The Era of Interfaces
Page 3: A Primitive Broker

How would you rate the quality of this article?
1 2 3 4 5
Poor      Outstanding

Tell us why you rated the content this way. (optional)

Average rating:
3.3 out of 5

6 people have rated this article.

      iPhone iPad Developers Conference

 

DevReach