UML for the Software Developer, Part 2: Mastering Associations

UML for the Software Developer, Part 2: Mastering Associations

lasses are only the first step to understanding UML. In my last article I discussed classes in UML and how they are coded using C#. In this article I will tie-up a few loose ends by describing how classes are arrived at and then march into how they relate to each other in UML using associations.

Setting the Stage with a Story
In software development methodology, stories are often used as a software metaphor; they are a narrative that is used to represent a conversation between the developer(s) and the customer. Stories owe their popularity to Extreme Programming and can be used as the basis for building UML classes and explaining how they relate. When using a story as the basis of your system, the first step is to convert the nouns in the story into UML classes. This will give scope to the system. As the story expands to include other stories, the new stories will map into different namespaces, each with its own scope.

In the last article I gave the example of RichMen Inc. It entailed a simple story about a brokerage firm that wanted a system to track what securities brokers bought and sold for their clients. Even with a story this simple I required three classes?Broker, Client, and Security&#151to represent the nouns in the story. The classes were encapsulated in the package Brokerage, which represented the brokerage firm, and I added some CRUD. (CRUD stands for Create, Read, Update, and Delete; it represents the typical data operations.)

In almost every system, whenever there is data there is a database. In the system I’m developing for this article I will add a database to store the data from my three classes.

Iteration 1: Adding a Database
If the system was only using the database to store and retrieve information I could directly access the database from each of the classes.

Figure 1. Adding a Database to the Example: A <> stereotype is the way that the database is identified.

In the last article Broker, Client, and Security inherited the addMember, deleteMember, and listMember methods from the BrokerageCollection interface (see Figure 1). Notice in Figure 1 the addition of a line attaching BrokerageDatabase to BrokerageCollection. This is called an association.

When two classes do not inherit from one another but are related to each other it is called association. In the weakest sense association is one class referencing another class. Often this is one class using the methods of the other class.

As developers, you and I know that in the example of the BrokerageCollection using the BrokerageDatabase (see Figure 2), we can use ADO and also that the database uses SQL. If you had previous knowledge of the environment, you might know whether the database is SQL Server or Oracle. This information could be added as an additional note on the diagram. You can infer from <> that BrokerageCollection references BrokerageDatabase. How do you know it is not the other way around? The answer lies in how style rules are used in UML.

Just as in programming, there are style rules in UML. One of these rules governs which way to read the diagram. I use the left-to-right approach for class diagrams, which have the boundary classes to the left (a boundary class is one that communicates with a device, actor, or role) and the classes that represent the back-end parts of the system such as the database, to the right. This is not universal. In component diagrams with layers in them the left-to-right is replaced by top-to-bottom?sometimes called a “stack.” (I will cover component diagrams in another article. For now, think of a component as container for related classes.)

Figure 2. Association Between Classes: The ends of an association are roles. In this example a description represents the roles. This is a good practice when there isn’t a descriptive role name.

The importance of style is to communicate these rules, which are sometimes idiosyncratic to others that need to use them. In project-level work this is either captured as an addendum or as a legend on the diagram.

Implementing the Association
In the current example the auto-generated code fails. The BrokerageDatabase does not translate into a C# class but represents the SQL Server database. Communication with a database requires a connection string instead of the reference from a C# class, which results from auto-generating the class.

BrokerCollection is also an interface. By associating BrokerCollection with BrokerDatabase, every class that inherits from BrokerCollection will also inherit the association. These issues force me to hand-code most, if not all, of the classes in the system (see Listing 1).

Because of the inheritance, the code for Broker, Client, and Security in Listing 1 are going to be the same. The extra coding required is tedious and error prone. It is also difficult to maintain the application when coded this way. If the structure of the database were to be changed, then Broker, Client, and Security would all have to change.

Iteration 2: From an Interface to a Façade
Interfaces come in two forms. The first type is the interface as a method template. Each class that inherits from the interface is required to implement all the interface’s methods. In the example, each class that was implemented had to implement a version of addBrokers, deleteBrokers, and listBrokers with the same method signatures, guaranteeing that every access to the database would have this functionality. (For you COM developers, implementing a COM interface was critical. By inheriting from the IUnknown interface and implementing QueryInterface, AddRef, and Release methods, a component could interact with another component by only knowing the other components’ IDs (GUID) in the operating system’s registry.) When we look at a class that inherits from an interface we have a common set of expectations that the classes will support the same functionality.

In the example, each class inherited from BrokerageCollection. The problem is that even though the functionality was guaranteed we had to implement that functionality for each class. Not only that but if the database location were to change, the structure of the database changes or the database would be split between two instances of a database (partitioning the database). Each class using this interface would have to rewrite these methods. What a mess!

Author’s Note: I once worked with a company that, after years of development, had not inherited from a common interface and had no common access to the databases. More than 100 databases were hard-coded among a few hundred applications. Changes to any of the databases would cause all types of unexpected problems. They finally added objects that encapsulated the database layer similar to what I’ve described in this article. The cost was 6 to 9 months of time for 10 developers and over $500,000 dollars. And it was still worth it.

To fix this problem I’ll use another type of interface called a façade. A façade is one of the main design patterns, documented by the Gang of Four in their seminal book “Design Patterns.” A façade acts as an interface between systems. I can add the façade to my current classes by adding a new class called, not unexpectedly, dbFacade.

Figure 3. The BrokerageDatabase Becomes dbFacade: The BrokerageDatabase required knowledge of how to implement it. dbFacade hides the implementation.

I’ve added dbFacade to my UML model (see Figure 3). Notice that the stereotype is <>. UML 2.0 has a specific way of defining patterns but it would have been beyond the scope to cover that here. For now the stereotype does the job. I’ve kept the interface class but dbFacade does not use it because each method has an extra parameter for the type. Remember that an interface’s method signatures have to match the inherited functionality and dbFacade’s doesn’t.

I can use my left-to-right style rule to eliminate the <> stereotype or I can use a navigation arrow on the association, as I did in Figure 3. I prefer using the left-to-right style rule because it forces the UML modeler to order its classes, but either way will accomplish the same thing. Code generators, however, use the navigation arrow, and if the arrows are not used they generate C# classes that reference each other.

The multiplicity isn’t required here either. If there are multiple instances of Broker, Client, or Security it isn’t evident from the design so far. It can be surmised that dbFacade will have a multiplicity of one. Most code generators will assume a multiplicity of one unless specified otherwise.

I removed the database class BrokerageDatabase. The code that will communicate with the database is all encapsulated in the dbFacade class leaving the connection to BrokerageDatabase purely an implementation detail.

The Refactored Implementation
So far, I’ve refactored the implementation without coding. To prove that this really is a better way of doing things, I’ll look at the refactored implementation of the classes.

using System;using System.Collections;public class Broker: implements IBrokerageCollection;{	dbFacade db = new dbFacade();		void addBroker(String Name){      dbFacade.addMember(Name, “Broker”);};	             void deleteBroker(String Name);	{                 dbFacade.deleteMember(Name, “Broker”);             }                         ArrayList listBrokers();             {                  Return (ArrayList) dbFacade.listBrokers(“Broker”);              }     public class Client (); // Add all of the same database logic as in Broker    public class Security(); // Add all of the same database logic as in Broker   }   }

If I were feeling ambitious there is some more refactoring that I could do. I could pass in “Broker” to the constructor and return a reference that would always use the broker table. The important accomplishment is the encapsulation of the intricate C# ADO code to a single class. With that done, the class can be assigned to a database specialist to code out the ADO, call SQL stored procedures, and find the most efficient method for implementing the database. Should that database change, only the database specialist is affected, leaving typical developers like you free to focus on the business logic.

Some Final Thoughts
In the process of adding an association you’ve learned:

  • what an association is and how it is implemented in code.
  • some more about interfaces and that interfaces can act as templates or patterns, such as the façade pattern which encapsulates implementation details.
  • about style and that diagrams do not always look the same even though the UML rules are still followed.
  • about refactoring and the time and effort that can be saved by accomplishing it before any code is written.
  • that by designing systems using some of the ideas discussed here the separation of concerns can be enforced.

The next step in the UML process is to learn about whole-part relationships. In UML, the whole-part relationship is represented by special associations called aggregation and composition. UML modelers will use either one or the other because of their similarity in code, however you will learn that there are differences and these differences can be critical to developing a system. It will be an exciting time as you begin to effect collaboration between the classes you’ve created in, using other classes and just plain having fun.

See also  Strengthening Business Cybersecurity With Threat Intelligence

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist