Defining Classes and Class Relationships
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


In this lesson, we'll discuss a conceptual model versus the actual design elements required by using an OOP language like C# to implement the final product. We'll use Unified Modeling Language (UML) tools built into Visual Studio to assist with the conceptualization and implementation of the model, designing our C# classes during the modeling process. We'll further discuss the concept of scope and visibility in the context of our model and classes, the relationship between our classes, and the operations between those classes as objects.

Conceptual Versus Design

In creating a C# application, we've considered a conceptual model of the components of the application as they might exist as objects in the real world. As we move from our conceptual model to designing the application using Visual Studio and C#, often the real-world model and the design implementation diverge. Why? There may be a number of reasons for the differences, such as:

Regardless, we will always try to have our model initially represent real-world objects as closely as possible. Modifications that deviate from this initial model should be documented as you work through the design implementation. So, let's create a new application that uses letters as stamps to create a stamp collection. Before we begin coding, let's see if we can design a "handwritten" model by determining the objects we need and how they relate to each other.

Defining a Class

What objects would compose a real-world stamp album? Primarily, there would be two objects: stamps and albums. In the real world, we might collect more than one stamp of the same kind, and we may need more than one album, as an album has a finite number of stamps it can contain. If we wanted to create an application that exactly duplicated what we might contain in the real world, then we would have to support the concept of a single stamp, with different properties, and multiple albums, each holding an instance of a stamp. Such an application would effectively serve as a catalog of the real stamps and albums we owned. But, we could also create a virtual stamp collection. Why virtual? Well, a virtual album has unlimited space, so we'd only need a single album. Also, our stamp collection may not be intended to represent every duplicate stamp, but only each unique stamp. Can you see how quickly our design can deviate from the objects in the real world? The final design depends on what we want the application to do, and how we might adapt the application to take advantage of a virtual representation versus a real-world application.

For our stamp collection, let's model the real world, making our application a catalog of our entire stamp collection. That means that rather than have, say, an "A" stamp, and a count of how many "A" stamps we have and treating them as if they were all identical, we look at each "A" stamp as an individual stamp, with properties that relate to each stamp, like the date it was released, when we acquired it, and so on. In the real world, a single album only holds so many stamps, so we'll have to include an album identifier, or number, and a way to represent a collection of stamps for each album, and a way to represent a collection of albums.

So, properties of our stamp class are:

Properties of the album class are:

With these two class definitions, let's use Visual Studio to create a class diagram of our two classes using the Visual Class Diagram tool. This tool helps us to visually create the classes, adding class variables (fields), properties, accessors (get and set methods), methods, and constructors, adding the appropriate code to our classes. Not only does the tool create a UML class diagram, it helps us to visualize the relationships between classes that we'll explore in the next section.

Create a new Visual Studio Windows Forms Application named AlphabetStamps. Click the entry for Form1.cs and replace it with AlphabetStamps.cs. Click the Form1 Form titlebar, and change its Text property from Form1 to Alphabet Stamps.

Click the View Class Diagram icon in the Solution Explorer.

Click to save your changes. You'll notice that the class diagram for the project already contains an item, known as a class icon. The class icon containing AlphabetStamps is the main class we work with when we create a Windows Form application, and is the class named Form1.cs by default, which we changed to AlphabetStamps.cs. Now, we'll add some classes of our own.

Right-click in the white space of the Visual Class Designer, and in the popup menu that appears, select Add | Class. In the New Class dialog box that appears, change the Class name: value to Stamp, then click OK. Add a second class named Album.

Let's add our class properties to both of the classes using the Class Details. The Class Details window includes both Properties and Fields. What's the difference? Fields are simply private class variables, whereas Properties include accessors (get and/or set methods). We could add all of these properties in the Properties section, but the default C# code that is generated isn't as optimal as if we add them as Fields and then Refactor them.

Select the Stamp class in the Visual Class Designer, then add each field using the Class Details as shown below.

Click to save the modified class diagram and Stamp class. Then, select the Album class in the Visual Class Designer, and add the fields as shown below.

Click to save the modified class diagram and Album class. You probably recognize the List<Stamp> syntax as a .NET Collection like we've used before. We'll use this collection to hold each Stamp object that a specific album contains.

We now have a stamp class we can use to create an instance of each stamp object. We also have an album class we can use to hold each instance of a stamp object for that album. What other objects might we need? In the real world, we would have our albums stacked nicely on a shelf, kept in a safe, or hidden somewhere safe from our siblings who can't wait to draw a funny mustache on a very expensive stamp. In our virtual stamp collection, we need another collection to represent the concept of storing our albums. We could create another class, perhaps called Albums, but for our current implementation, all we really need is another List collection. We'll add this collection to our AlphabetStamps class.

Select the AlphabetStamps class in the Visual Class Designer, then add a new private field named _albums of type List<Album>, as shown below.

Click to save the modified class diagram and AlphabetStamps class. Using tools like the Visual Class Designer helps you to visualize your classes and objects as you attempt to model the real world. (Of course, you have the freedom to design your model however you like, even if it doesn't represent the real world, although approximating the real world helps you and others to more easily understand your code and the classes and objects you design.) The Visual Class Designer is creating your underlying code, so let's take a look at the two new classes, and the changes we made to the AlphabetStamps Form class.

Double-click the Stamp.cs entry in the Solution Explorer, and examine the generated code in the Visual Code Editor. Repeat this process for the Album.cs entry, and for the AlphabetStamp.cs entry (for this entry, you'll need to right-click it and select View Code, as it also has a design component, the Form).

As you open and examine the generated code, you'll notice the three-slash (///) character blocks. These sections are part of Studio's ability to generate documentation for your source code. We'll discuss this later in more detail.

Before we add the "sets" and "puts" (accessors) using refactoring, let's discuss class relationships.

Defining Relationships

When you click on the Visual Class Designer (the ClassDiagram1.cd tab), the Studio Toolbox displays tools you can use when visually creating classes:

As with other Toolbox tools, you can drag and drop certain tools onto the Designer, but a couple of these tools, when selected, allow you to create relationships between classes and class fields. What do we mean by relationships? Consider objects in the real world, and how we define them. For example, all trucks are vehicles, but not all vehicles are trucks. Some might be cars, motorcycles, or something else entirely. When we think of objects, often it's helpful to think in terms of one object being of a type of another object, an is-a relationship: a truck is-a vehicle. Another relationship is often described as a has-a relationship: a car has-a steering wheel. In our current project, we have one is-a relationship: our AlphabetStamps class is-a Form. We also have two has-a relationships: our Album class has stamps, and our Albums collection has individual albums.

We use different kinds of visual cues to show these relationships in the Designer. An is-a relationship (also called inheritance), is indicated by a line with a closed arrow next to the parent class. A has-a relationship (also called association), is indicated by a line with an open arrow. Associations indicate that the datatype of a field is of a class we've defined. Associations that involve collections are called collection associations, and use two open arrows. Short descriptive information is also typically displayed next to the assocation line to help describe the relationship.

Our Class Diagram does not currently contain any visual associations, so let's display the associations that we've already created. Although the Class Designer visual display will change to display these associations, no underlying code changes will occur. In future lessons, we'll further explore using the Visual Class Designer to create these associations using Toolbox Class Designer items.

Right-click on the AlphabetStamps class and select Show Base Class. Next, right-click on the _albums field and select Show As Collection Association. Then, right-click on the _stamps field Album class and select Show As Collection Association. Your design view should look like this:

As you can see, adding the inheritance and association lines enhances the visual representation of the class diagram. Let's discuss a few other issues related to class diagrams.

Visibility and Cardinality

An important feature of a UML class diagram is the ability to easily determine and recognize the visibility of elements of a class, such as fields and methods, by using access modifiers. Typically, in a standard UML class diagram, public access is visually illustrated using a plus symbol (+), private access using a minus symbol (-), and protected access using the pound or hash mark (#). In the Visual Class Designer, visibility is portrayed by the symbols to the left of each element of the class. For protected, a small gold key would be shown. For private, a small padlock, and for public, there would be no extra symbol. Below is an image of three class variables we've added to the AlphabetStamps class for illustration purposes only to show each of these three scenarios, and the accompanying symbols. In the image, you'll notice we also included the Class Details that includes the access modifiers. Do not add these fields to your class.

Another very important concept that a UML class diagram should portray is cardinality, also known as multiplicity. This information indicates the relationship between classes, showing the number of instances of one class when linked to a single instance of another class. For example, in our model, one album will have multiple stamps (or none), and one stamp will belong to only a single album. The Visual Class Designer currently has no mechanism for depicting this relationship. The typical symbols used to depict cardinality in a UML class diagram are:

IndicatorMeaning
0..1Zero or one
1One only
0..*Zero or more
1..*One or more
nOnly n (where n > 1)
0..nZero to n (where n > 1)
1..nOne to n (where n > 1)

So what would our class diagram look like if we could add cardinality? The image below shows the cardinality relationship between the stamp and album class, and the relationship between the albums collection and the album class. Note that an album does not require a stamp to be an album, expressed using the 0..* indicator, but that an albums collection requires at least one album, expressed using the 1..* indicator.

To go through all of the features UML can represent could easily require a course of its own, but for now, we've shown a few of the fundamentals provided by Visual Class Designer. We've also illustrated how the Visual Class Designer aids in producing your code. What about the refactoring we'd mentioned earlier? Let's refactor one class variable (or field), turning the field into a property by adding the accessors for the field.

Right-click on the Stamp class _letter field, and select Refactor | Encapsulate Field. In the Encapsulate Field dialog box, accept all of the defaults and click OK. In the next dialog that appears, click Apply. Next, double-click on the newly added Letter property to open the Visual Code Editor for the Stamp.cs class.

When you view the accessors in the Visual Code Editor, you should see the code highlighted below has been added to allow access to the _letter field. Using refactoring is a great way to convert fields into properties!

OBSERVE: Stamp.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AlphabetStamps
{
    public class Stamp
    {
        /// <summary>
        /// Stamp letter
        /// </summary>
        private string _letter;
        
        public string Letter
        {
            get { return _letter; }
            set { _letter = value; }
        }
        /// <summary>
        /// Stamp released date
        /// </summary>
        private DateTime _releasedDate;
        /// <summary>
        /// Stamp acquired date
        /// </summary>
        private DateTime _acquiredDate;
        /// <summary>
        /// Stamp has visible post mark
        /// </summary>
        private bool _hasPostmark;
    
    }
}

Congratulations! You've completed this lesson on defining classes and class relationships. You should have a better understanding of UML class diagrams and using the Visual Class Designer to create your classes!

Before you move on to the next lesson, do your homework! Right-click in the window where this lesson text appears and select Back. Then select the Quiz(zes) for this lesson in the syllabus and answer the quiz questions. When you finish the quiz questions, click HAND IT IN at the bottom of that window. Then do the same with the Project(s) for the lesson. Your instructor will grade your quiz(zes) and project(s) and provide guidance if needed.