Virtualization is a simple concept that can be used in extremely complex ways. But in its simplest form it dictates how well-structured Windows programs should be designed, and understanding the basic concept is important in working out the best way to tackle a programming problem.
.Net is all about objects. Some of those objects – such as controls on a form – have a presence that is about as concrete as you can get in a software application. If you add a text box to your form you have something in Designer that you can manipulate with the mouse. When the application runs you have a component on your form which the user can interact with. It’s a very real thing.
A timer falls into a slightly more abstract category. You can still add it to the form and set its properties in the Designer, but it does not have a visible presence in the running application. But it is just as much a component of your application as the text box is.
Then there are objects you create within your code. These are even more abstract, but they are just as much a real object as the more visible components, and they are just as important to the program structure.
Why this discussion of the abstract nature of objects? Because Virtualization refers to the process of constructing an abstract version of the ‘real’ object in order to provide the functionality that is most convenient for use within the application. In the computing world the ‘real’ object us usally a piece of hardware that is virtualized in code. But in .Net programming the ‘real’ object is something that the user can see and interact with in the applicaiton, and the virtual object is a version of the relevant parts of that real world object that is actually used and manipulated in the code.
Take an example of a very simple program that will mutiply two numbers together. The program will have three text boxes and a button. When the user clicks the button, the numbers in two of the text boxes will be multiplied, and the result displayed in the third text box. At least, that’s what the user will see.
If the application was programmed like that, the code would be:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TextBox3.Text = Val(TextBox1.Text) * Val(TextBox2.Text)
That code can be made to run, but it has has all sorts of problems. The problems can be overcome by virtualising the process – creating an alternative world within its own little capsule where things exist in the most convenient form and where they can be properly managed. Of course, there has to be communication between the ‘real’ world and the ‘alternative’ world, and in practice (and certainly in this simple example) implementing this communication can be a major part of the total task. But by defining and enforcing the standards for that communication we are creating a well-structured OO program.
This is the (slightly artificial) alternative with the calculation virtualised into its own private routine.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim A As Double Dim B As Double If Not Double.TryParse(TextBox1.Text, A) Then MsgBox("Invalid number. Please enter a valid number") Exit Sub End If If Not Double.TryParse(TextBox2.Text, B) Then MsgBox("Invalid number. Please enter a valid number") Exit Sub End If Dim C As Double = Multiply(A, B) TextBox3.Text = C.ToString End Sub Private Function Multiply(ByVal Arg1 As Double, ByVal arg2 As Double) Return Arg1 * arg2 End Function
The code now has two distinct parts. The process of validating the text box entries and converting them to numbers, and then converting the result back to a string, represents the communication between the virtual world and the ‘real’ world. The virtual world (trivial in this example) deals only in numbers and knows nothing about where they come from or what will be done with them. It only knows that there will be two values that need to be multiplied together. It is a virtual representation of three values in three text boxes, but it exists in a form that is compatible with the action that needs to be carried out.
The parallel between virtualization and encapsulation should be apparent. Virtualization almost always involves some degree of encapsulation. But it is more than just encapsulating the code. It is the deliberate conversion of ‘real-world’ objects and values into a form that is manageable in code and convenient for the operations that need to be performed.
The importance of virtualisation is often most apparent in graphical applications. An example is any board game. Many beginner programmers lay out their game board in the designer, perhaps using picture boxes, and then attempt to manipulate the picture boxes as part of the game play. When the user clicks on a particular picture box, that becomes the current game piece; when they drag the mouse, that picture box is moved along with the mouse cursor; when they stop dragging, one or more picture boxes get updated.
That approach can be made to work, but it quickly becomes unmanageable. For instance, detecting illegal moves, determining a winning move, saving and restoring the state of the game, or trying to program an intelligent opponent who needs to keep track of past moves, quickly turns into a nightmare of convoluted code. The reason is that, while picture boxes are very good at showing the game board to the user, they are not good at communicating the information needed for the game play. Finding the image associated with a picture box in order to determine what piece just got moved is a good example of how inappropriate a picture box is for the game play object.
By virtualising the game board the programmer can create and use objects that are specifically designed to support the game play. The programmer can use those objects in the most conveient format – for instance in a 2-dimensional array. The game play code is then enormously simplified. To communicate between the ‘real’ game and the virtual game the programmer needs to write code that
- Converts a mouse position (click or drag) into game cell references
- Displays the current state of the game
Both those tasks often turn out to be much simpler than trying to keep the game display updated directly from the user input. Displaying the current state of the game, in particular, can often be implemented as a simple loop that cycles through the game board cells and draws each one (incidentally, the same process that has to be done on a form refresh anyway).
For an example of a virtualised game board see the Concentration example. Note how the mouse clicks are converted to game cell references, and how the game board cells are redisplayed as required.
A similar situation can be demonstrated in the common task of managing a list. If an application has a listbox that contains a collection of objects, the temptation is to manipulate the listbox collection – adding, deleting, searching etc – as the user makes various choices. The application starts out simple and manageable, but soon starts to get complicated when additional features are added. This complexity begins to overwhelm a beginner programmer. A better approach is to create a collection that contains the actual objects to be manipulated, choosing a form of collection that is best suited to the requirements of the code. That way, the listbox is simply the ‘real-world’ representation of the virtual collection of objects that is maintained within the application. When the user takes an action, the virtual object gets updated, and the listbox items are re-created from the object list. It’s easy to see how this design would simplify tasks such as
- Maintaining the listbox as a subset of the object list (for fast updating)
- Maintaining the listbox in a user-selected sequence while the object list is kept in a sequence most suitable for processing
- Maintaining the object list in a different type of structure (such as dictionary)
- Saving and restoring the object list.
and so on. The communication between the real-worl object and the virtual object would involve converting a list box selection into one or more items inthe object list, and then redisplaying the listbox by iterating over the object list – both relatively simple tasks.
See the List Handling example for the details of virtualising a listbox.
Virtualising the data represented by graphical (or other ‘real-world’ ) objects enables you to represent that data in the form that is most convenient for processing. It does involve some work in translating the data back and forth between the real world and the virtual world, but the effort invovled in that task is more than outweighed by the simplifications that are possible as a result of processing the data when it takes a form that is suited to the processing being applied.