As described earlier, the purpose of Class Designer can be stated in two ways: as a tool for visualizing existing code, and as a tool for defining the structure of that code in the first place. In this section, we'll look at the visualization of existing code.
In fact, version 1 of Class Designer has been optimized for several usage scenarios, including visualization of existing code (this section), refactoring (Chapter 11), and drawing diagrams for documentation purposes. In addition, of course, is top-down class design, which is the subject of the section "From Class Diagrams to Code."
As a representation of types, the class diagram offers a pictorial alternative to the Object Browser for examining types. You can visualize types that are not your own — i.e., those from third-party assemblies, including the .NET Framework itself — and you can tailor the visualization to show more or less detail, including the important relationships between the types. The selection and arrangement of types on diagrams should communicate something extra, over and above what would be deduced from the Object Browser.
The StockBroker example that runs through the previous chapters will provide a basis for visualizing existing code using class diagrams. We'll start by drawing a pictorial representation of the UI classes for one of the StockBroker client applications: DealingApp.
Figure 5-2 is a class diagram based on the UI components of the DealingApp Windows application implemented in Chapter 2.
You don't need to follow these steps yourself, but they demonstrate how we created that diagram. Here's what we did:
Within our fully implemented StockBroker solution, we created a new blank class diagram by right-clicking the DealingApp project and choosing Add New Item from the context menu.
We dragged the DealingForm.vb file from the Solution Explorer to the class diagram, resulting in the DealingForm class shape being displayed.
We expanded the DealingForm class shape to reveal its members, and for the three Button members — BuyButton, QuoteButton, and SellButton — we right-clicked and chose Show As Association… to reveal three links to instances of the separate Button class.
We right-clicked the Button class shape and chose Show Base Type to reveal the ButtonBase superclass from which Button inherits.
We expanded the Button class shape to reveal its members, right-clicked the DialogResult member, and chose Show As Association… to reveal a link to an instance of the DialogResult Enum.
We'll tell you about the Show as Association and Show Base Type features more formally later. The important thing to understand now is that Class Designer has been used as a tool for inspecting types and their relationships, very much as you would use the Object Browser, although in this case we have specifically drawn a picture showing only the types we want to show, to the level of detail we are interested in, and in a form that we can share with other developers as printed copy.
We have built a picture of the UI classes in our application merely by exploring relationships from the single starting point of the DealingForm, a class that was generated originally from our Application Design, and whose members are instances of .NET or other library types — not types of our own making.
We can use Class Designer to provide a diagrammatic representation of auto-generated classes and those belonging to compiled assemblies, but what about new classes of our own making?
You might recall that in Chapter 2 we invited you to change the return types of the DealingService buyStock and sellStock web methods from integers to StockSale and StockPurchase types. That was to demonstrate the synchronization between the code and the model, whereby changes that you made in code were reflected in Application Designer's Web Service Details window.
We'll take that idea further by conceiving three new classes: StockPurchase, StockSale, and StockDeal, which provides a common base class for both kinds of deal. Let's look at the code for the three StockDeal classes.
The StockPurchase class would be returned by the buyStock web method of the DealingService. The code for that class would be as follows:
Public Class StockPurchase Inherits ClassLibrary.StockDeal Private mNumberOfSharesBought As Integer Public Sub New(ByVal stockSymbol As String, ByVal numberOfShares As Integer) MyBase.New(stockSymbol) mNumberOfSharesBought = numberOfShares End Sub Public ReadOnly Property NumberOfSharesBought() As Integer Get Return mNumberOfSharesBought End Get End Property Public Overrides ReadOnly Property NumberOfShares() As Integer Get Return NumberOfSharesBought End Get End Property End Class
The main points to note in the preceding code are as follows:
The StockPurchase class inherits from the StockDeal base class (defined later).
There is a member variable — mNumberOfSharesBought — to hold the number of shares bought in the purchase.
The constructor takes in a stockSymbol and numberOfShares, storing the number of shares in the local member variable and passing the stockSymbol through to the base class constructor.
There is a read-only property named NumberOfSharesBought that returns the contents of the mNumberOfSharesBought member variable.
An additional read-only property named NumberOfShares returns the value of the NumberOfSharesBought property, thereby implicitly also returning the value of the mNumberOfSharesBought member variable. Take note of the name of this property — NumberOfShares rather than NumberOfSharesBought — and note the inheritance modifier of Overrides.
The StockSale class would be returned by the sellStock web method of the DealingService. The code for that class would be as follows:
Public Class StockSale Inherits ClassLibrary.StockDeal Private mNumberOfSharesSold As Integer Public Sub New(ByVal stockSymbol As String, ByVal numberOfShares As Integer) MyBase.New(stockSymbol) mNumberOfSharesSold = numberOfShares End Sub Public ReadOnly Property NumberOfSharesSold() As Integer Get Return mNumberOfSharesSold End Get End Property Public Overrides ReadOnly Property NumberOfShares() As Integer Get Return NumberOfSharesSold End Get End Property End Class
That code is pretty much identical to the code of the StockPurchase class except that the member variable is named mNumberOfSharesSold and its wrapper property is named NumberOfSharesSold.
The two classes that we've reviewed both inherit from the base class StockDeal, the code for which is as follows:
Public MustInherit Class StockDeal Private mStockSymbol As String Public ReadOnly Property StockSymbol() As System.String Get Return mStockSymbol End Get End Property Public Sub New(ByVal stockSymbol As String) mStockSymbol = stockSymbol End Sub Public MustOverride ReadOnly Property NumberOfShares() As Integer End Class
The main points to note in the preceding code are as follows:
The StockDeal class is defined as MustInherit, thus making it an abstract class.
There is an mStockSymbol member variable to hold the symbol of the stock to which a deal relates, with a read-only property that returns it. This functionality is provided entirely by this base class, on behalf of the StockPurchase and StockSale subclasses.
The constructor takes a stockSymbol and sets the member variable, with this constructor invoked from the constructors of the subclasses.
A read-only property named NumberOfShares, with inheritance modifier MustOverride, is declared but not implemented. The idea is that any kind of StockDeal — whether StockSale or StockPurchase — can report the number of shares involved in the deal regardless of whether they are shares bought or shares sold. The subclasses take care of returning the correct value in each case.
If you like, you can attempt to retrofit those classes into your implementation of the StockBroker application and its clients. It's not compulsory, of course, and for the purposes of this chapter it is sufficient to simply create a new Visual Basic Class Library project containing those classes, as we have.
Now we'll create a class diagram to show those classes diagrammatically.
Assuming you have the three classes contained within a class Visual Basic Class library, you can create a new class diagram simply by right-clicking the project and choosing Add New Item from the pop-up menu. Select the Class Diagram item and name it StockDeal.cd, the.cd extension marking this as a class diagram file.
You could also do this from the StockBroker project if you decided to follow the optional exercise and retrofit these classes into your StockBroker application.
Now all you need to do is drag the three classes from the Class View, or the three source files from the Solution Explorer, onto the diagram, for the result shown in Figure 5-3. You could also build that diagram by multiple-selecting the three classes in Class View or Solution Explorer, right-clicking, and choosing View Class Diagram from the context menu — without having pre-created the blank diagram at all. In that case, the diagram will be given a default name.
You can also create an auto-populated diagram to show all of the classes in a particular project, or all of the classes within a specific namespace. Just right-click the project or namespace as appropriate and choose View Class Diagram from the context menu.
The main points to note on the diagram are as follows:
Each class is shown with compartments for its Fields (i.e., member variables), Properties, Methods, and Events in a style similar to the UML notation for attributes and operations on a class. If you don't see those compartments, try clicking the scroll-up and scroll-down buttons (shown as chevrons in the top-right corner of each class).
The inheritance relationships between the two subclasses and the base class have been drawn automatically, using notation similar to UML generalization notation.
Because the StockDeal base class must be inherited, it is rendered with a dashed outline to distinguish it as an abstract class.
What you've achieved by dragging those classes onto the diagram is equivalent to reverse engineering a set of source code classes into a class diagram, except that you haven't had to perform any explicit "reverse engineering" step at all. Whereas some modeling tools require you to invoke a specific reverse-engineer option to achieve this, Visual Studio 2005 does not. All classes shown in the Class View are immediately available for representation on class diagrams; and for classes taken from compiled assemblies, the original source code need not even be present.
Members of types are displayed in compartments according to what kind they are. Figure 5-3 shows members grouped as Fields, Properties, and Methods. Alternatively, you can choose to group members by access modifier; or sort them alphabetically, simply by selecting the menu option Class Diagram Group Members Group by Access or Class Diagram Group Members Sort Alphabetically.
You can hide selected members by right-clicking a member and choosing Hide; then right-click the member's compartment heading and choose Show all Members to see it again. You can also collapse and expand entire compartments, such as all fields or all methods, or all public members or all private members, depending on what is on view. Choosing to hide members, or to collapse compartments, has no effect on the underlying code whatsoever, so these operations are fully reversible.
As you work with visualizing members, you will notice that the set of compartments varies according to the language of the underlying code.
Historically, UML notation does not provide separate compartments for some kinds of members such as properties, so UML tools such as Visio for Enterprise Architects and Rational XDE indicate properties as stereotyped attributes, or as pairs of get/set methods. This demonstrates one of the benefits of Class Designer's domain-specific notation over a generic design notation such as UML.
On some of the class diagrams in this chapter, such as Figure 5-3, member names are shown but member types are not. On other figures, such as Figure 5-4, the type of each member is shown along with its name. In Figure 5-2, method members are shown with their full signatures. You can toggle these display settings by selecting Class Diagram Change Members Format and choosing Display Name, Display Name and Type, or Display Full Signature.
If you choose to save space on diagrams by showing only member names, you can still see the complete type and signature information for a member by hovering over a member to see that additional information as a tooltip.