Five Great Patterns for Dealing with References

Five Great Patterns for Dealing with References

n a UML class model, classes are described by having attributes, operations, and relationships with other classes. When such a model is converted into code, both the public attributes and the relationships will appear as properties in your classes. These properties have types, which generally fall into one of the following categories:

  • Basic types. Simple properties such as FirstName, StartDate, or Limit have simple property types, such as string, datetime, or int.
  • Value types. Properties such as Email, ZIP, or CreditCard are a bit more sophisticated. Such properties tend to enforce validations on the possible values of properties, including regular expression and range checks. Therefore, types of such properties are described in value types, such as BankAccount, ZIP, or CreditCard.
  • Business class. Properties of classes that implement a relationship with another class in general have the type of that class, either representing a single instance or a collection of instances of the related class. A Project class, for instance, is likely to have a property Manager of type Employee.
  • Reference types. When the value of a property comes from a limited collection of values, the type of that property uses a reference type. There are several implementations for reference types, ranging from enumerated types, such as CourseLevel in a Course class to small referential classes, including a Country or Currencies class.

At first glimpse, enumerated types and referential classes appear to be to the most obvious patterns for implementing reference types, but there are other patterns that can do the job for you. This article includes five such patterns; I’ll show you when each of these patterns is applicable and useful.

Each pattern is measured up against a number of criteria:

  • Type safety. When referring to the property involved, is this type safe? When the type of the property is passed as an argument in an operation, can only values from the limited collection be used? Is it possible to type check values in code, without making the code vulnerable to typing errors? For instance
       if (MyCourse.CourseLevel == CourseLevels.Expert) { … }

    is not really equivalent to

       if (MyCourse.CourseLevel == "Expert") { … }

    The second example relies on perfect user input in order to be validated. But eventually, someone will make a typo.

  • Collection of values. When developing a user interface, quite often all possible values from the collection of the property’s type need to be displayed, for instance in drop-down lists or radio buttons. How much programming effort is involved in retrieving all possible values for a certain reference type?
  • Display values. In some cases, it is desirable to display different values than the technical values that are used in your code. For instance, an enumeration CourseLevel might define a value VeryHigh, but this might appear on screen as Very high.
  • Flexibility. Can the values of the elements in the collection change without changing or breaking the code? Is the number of elements fixed or can new elements be added without endangering the application code?
  • Extending reference types. In some cases, it is useful to define a referential type, and than later be able to inherit from it and extend it with new values. Suppose you’re using a framework that uses a reference type Actions that contains all actions the framework is aware of. Developers might need to use both existing functionality as well additional Actions that aren’t yet defined. They can get exactly what they need by extending Actions.

Now, let’s look at a number of different patterns for dealing with references, and monitor how these patterns behave to the aforementioned criteria.

Enumeration Pattern
When you have a fixed number of elements to choose from, an enumeration is a suitable pattern for implementing references. An enumeration is a hard-coded collection of values. The enumeration CourseLevels in the following code is an excellent example.

   public enum CourseLevels   {      Beginner,      Advanced,      VeryHigh,      Expert   }

Using an enumeration is easy, as is shown in the next code example.

   public class Course   {      …      private CourseLevels level = CourseLevels.Beginner;      public CourseLevels Level      {         get { return level; }         set { level = value; }      }   }

Enumeration is a very elegant pattern, when the property’s value needs to be checked in your application code, as the following code example shows.

   if (MyCourse.Level == CourseLevels.Beginner) { … }

Methods on the Course class can be specified elegantly as follows.

   public bool HasLevel(CourseLevels level)   {     return (Level == level);   }

It is also easy to retrieve all possible values of an enumeration. In C# a simple call to CourseLevel.GetNames() or CourseLevel.GetValues() will do. Although using an enumeration is very straightforward, the pattern has a few issues. When its values need to be displayed on screen, there is no possibility of using different values than the ones defined by the enumeration. For sure, VeryHigh will appear as VeryHigh in your drop-down list.

Furthermore, if somehow the value of an element needs to be changed or new elements need to be introduced, enumerations lack the flexibility to perform this operation without recompiling the application. And third, in .NET enumerations are implemented as value type, which means that there is no way of extending them.

Constant Collection Pattern
To get around this third issue, developers tend to use a second pattern for using references, which I call the constant collection. In this pattern the collection of possible values is implemented as a class with a number of constants defined as its fields, much like in the following code example. Such a class can easily be inherited from.

   public class CourseLevels   {      public const string Beginner = "Beginner";      public const string Advanced = "Advanced";      public const string VeryHigh = "Very high";      public const string Expert = "Expert";   }

The constant collection pattern also alleviates the second issued raised by the enumeration pattern. When displaying values on screen the actual value of the constants can be used, thus allowing for VeryHigh to be displayed as Very high. However, in return, the constant collection pattern raises its own issues. First of all, one can only retrieve all possible values using reflection. This may not be very desirable.

Worse, constant collections are not always type safe, even if they appear to be at first sight. Because the constants are defined static, the constant collection is type safe in respect to specific checks, like this one:

   if (MyCourse.Level == CourseLevels.Beginner) { … }

But beware: Methods on the Course class will have to be specified with a signature as follows.

   public bool HasLevel(string level)   {      return (Level == level);   }

Thus, there is no guarantee that only type-safe values are passed, as with an enumeration. Both statements in the following code example will return true when MyCourse has level Advanced. And even worse, the third statement will not produce a compiler error, as one would hope, since Easy is not a valid value.

   bool hasLevel = MyCourse.HasLevel(CourseLevels.Advanced);   bool hasLevel = MyCourse.HasLevel("Advanced");   bool hasLevel = MyCourse.HasLevel("Easy");

Descriptor Pattern
The third pattern, the descriptor pattern, mixes best practices from the previous two patterns. Basically, a descriptor is a collection of instances of the class itself. Using this pattern, the CourseLevels class can be implemented as follows.

   public class CourseLevels   {      public static CourseLevels Beginner = new CourseLevels();      public static CourseLevels Advanced = new CourseLevels();      public static CourseLevels VeryHigh = new CourseLevels();      public static CourseLevels Expert = new CourseLevels();   }

Because the instances are again static, descriptors are type safe, both in simple checks as in operation signatures, just like enumerations, as the following example shows.

   if (MyCourse.Level == CourseLevels.Beginner) { … }

Methods on the Course class can also be specified elegantly.

   public bool HasLevel(CourseLevels level)   {     return (Level == level);   }

Also descriptors can be extended, like constant collections. In the simple implementation above however, it is not possible to use display values other than the defined names of the instances. Luckily a straightforward extension to this implementation solves these issues, if necessary.

   public class CourseLevels   {      public string DisplayValue;      public CourseLevels(string displayvalue)      {         DisplayValue = display;      }      public static CourseLevels Beginner = new CourseLevels("Beginner");      public static CourseLevels Advanced = new CourseLevels("Advanced");      public static CourseLevels VeryHigh = new CourseLevels("Very high");      public static CourseLevels Expert = new CourseLevels("Expert");   }

Still, descriptors have a fixed number of elements, just like enumerations and constant collections. And again, like the latter, retrieving the set of possible values requires reflection. In C#, in both patterns, an operation like the following takes care of this job.

   public static CourseLevels[] GetValues()   {      ArrayList list = new ArrayList();      foreach (FieldInfo pi in type.GetFields())      {         list.Add((CourseLevels) pi.GetValue(type));      }      return (CourseLevels[]) list.ToArray(typeof(CourseLevels));   }

Type Safe or Flexible?
So far, the patterns introduced are very useful when it comes to type safety. To enable this safety net the specific reference elements in these patterns are hard coded. However, situations may occur where type safety is less important than having the flexibility to change and add new reference elements without having to change the code. A simple example for this is reference types such as Countries or Currencies. Although the number of countries is not subject to many changes, whenever a new country emerges, it needs to be added immediately to the collection of elements. Thus, a second species of reference patterns arises, in which flexibility prevails security.

Small Business Class Pattern
Suppose you have a Location class that specifies a location where courses are held. Such a class would have a property Country that refers to a specific instance of a reference type Countries, such as in the next code example.

   public class Location   {      public string Name;      public string Address;      public string City;      public Countries Country;   }

In this implementation of the Location class the property Country can be set to any valid instance of the Countries class. The Countries class is a small business class. New elements are hardly ever entered, existing elements rarely change and small business classes are mostly used as references. In most cases, the data of instances of small business classes are kept in a database, each small business class having an associated table in the database. Regardless of how the data is retrieved from the database, a small business class may look something like this.

   public class Countries   {      public string Name;      public string Description;      public string CountryCode;      …      public static Countries New()      { … }      public static Countries Get(ID id)      { … }      public bool Delete()      { … }      public bool Update()      { … }      public static Countries[] All      {         get { … }      }   }

It goes without saying that display values for small business class can be combined from any of the available fields. Preferably, you could override the ToString() operation, and use this.

   public class Countries   {      …      public string ToString()      {         return Name + “ (“ + Code + “)”;      }   }

Note that in the database, each record in the Location table holds a foreign key to an associated primary key in the Countries table. Using a small business classes as reference allows for great flexibility. That is, it is easy to change or add an element, without having to recompile the application. Another benefit of using small business classes is that retrieving the collection of all instances is straightforward. It is not unlikely that a small business class holds a static property All (or likewise operation) that returns the collection of all countries, as an array of instances of Countries. And more, it is easy to inherit from a small business class and add additional functionality.

Using a small business class also has some drawbacks, especially when type-safe validations needs to be performed on specific fields of the class, like in the following code example from the Location class. In this example, a string is used for implementing the validation, again allowing typing errors?recall the constant collection.

   public class Location   {      …      private Countries country;      public Countries Country      {         get { return country; }         set { if (value.CountryCode != “NL”) country = value; }      }   }

If type safety is required, the Countries class can be extended with a number of static instances that can be used in validations. In the following code example the instance Netherlands is predefined.

   public class Countries   {      …      public static Countries Netherlands = Countries.Get("NL");   }

Now validation can be done using this static instance, hence it has the same syntax as validation using an enumeration or descriptor.

   public class Location   {      …      private Countries country;      public Countries Country      {         get { return country; }         set { if (value != Countries.Netherlands) country = value; }      }   }

Note that, since the instance Netherlands is declared static, the associated record will only be retrieved once from the database?when the first validation is executed. This extension technique is only useful when only a limited number of validations need to be performed. And although it offers more possibilities for static checking, it diminishes the flexibility of the solution, because these static instances may never be removed from the underlying table.

There’s always a trade-off between usability (in code) and flexibility (in use). You’ll find this trade-off is the main criteria when choosing one of these patterns.

Smart Reference Pattern
In the last pattern, a single small business class is defined. This single small business class is used to refer to any reference type that can be expressed in the properties of that class. Typically such a class, called a smart reference, has properties such as Name and Description. The single-most important property of a smart reference however, is the Type property, which declares which specific reference type is meant.

The Type property can best be expressed using an enumeration, for instance called ReferenceTypes, as in the following code example. Using this property, operations such as All() and Default() can be defined for retrieving all instances of SmartReference for a specific type, or even the default instance. Such operations can be declared both static and not static, either specifying the type, or using the instance’s Type property.

   public enum ReferenceTypes   {      Countries,      Currencies   }      public class SmartReference   {      public string Name;      public string Description;      public ReferenceTypes Type;      …      public SmartReference[] All()      { … }      public static SmartReference[] All(ReferenceTypes rt)      { … }   }

This last pattern is extremely flexible. New elements can easily be added to any of the types in the ReferenceTypes enumeration. The main benefit of using a smart reference over a number of small business classes is that it saves you a lot of programming effort. Maintenance functionality for all your reference types consists only of a single form, allowing the user to select a reference type and maintain the elements of that type.

However there is a small price to pay for this maintenance flexibility. Using a smart reference does not automatically ensure type-safe validation. A property of type SmartReference will need to perform additional reference type validation, as demonstrated here:

   public class Location   {      …      private SmartReference country;      public SmartReference Country      {         get { return country; }         set { if (value.Type == ReferenceTypes.Country) country = value; }      }   }

Also the display value for a SmartReference is limited to the fields defined by the SmartReference class. Again, I suggest using the ToString() operation to set the display value to any combination of fields.

When to Use Which Pattern?
Using references is inevitable when using business classes. However, there a lot of different implementations, all of which are variations or extensions to one of the five patterns mentioned above. The big issue is when to use which pattern. Table 1 sums up the characteristics for each of the patterns using the criteria defined earlier.

Table 1. Characteristics of each of five different patterns and their key criteria.

Type safety Collectionsof values Display values Flexibility Extensibility(inheritance)
Enumeration Yes Yes Limited to enumerated values Fixed, change of code required No
Constant collection Not when used as parameter Yes, using reflection Limited to defined values Fixed, change of code required Yes
Descriptor Yes Yes, using reflection Yes Fixed, change of code required Yes
Small business class Yes, when defining static instances Yes, easily retrieved from database Any combination of its fields Flexible, alter dedicated table Yes
Smart reference No, anonymous Yes, easily retrieved from database Any combination of its fields Flexible, alter single table Yes

The first three patterns are best in situations where a limited and fixed number of elements are expected. The three patterns mainly differ in that different display values can be used or where extensibility by inheritance is required. The last two patterns are best used in situations where there is a large collection of elements, and this collection is unlikely to change more often than the code is put in production. This requires the elements to be stored externally, rather than being hard coded, most commonly in a database.

Together, these five patterns, and the numerous variations and extensions to them, will give you control over the references you need to deal with in your projects.

devx-admin

devx-admin

Share the Post:
Clean Energy Adoption

Inside Michigan’s Clean Energy Revolution

Democratic state legislators in Michigan continue to discuss and debate clean energy legislation in the hopes of establishing a comprehensive clean energy strategy for the

Chips Act Revolution

European Chips Act: What is it?

In response to the intensifying worldwide technology competition, Europe has unveiled the long-awaited European Chips Act. This daring legislative proposal aims to fortify Europe’s semiconductor

Revolutionized Low-Code

You Should Use Low-Code Platforms for Apps

As the demand for rapid software development increases, low-code platforms have emerged as a popular choice among developers for their ability to build applications with

Global Layoffs

Tech Layoffs Are Getting Worse Globally

Since the start of 2023, the global technology sector has experienced a significant rise in layoffs, with over 236,000 workers being let go by 1,019

Clean Energy Adoption

Inside Michigan’s Clean Energy Revolution

Democratic state legislators in Michigan continue to discuss and debate clean energy legislation in the hopes of establishing a comprehensive clean energy strategy for the state. A Senate committee meeting

Chips Act Revolution

European Chips Act: What is it?

In response to the intensifying worldwide technology competition, Europe has unveiled the long-awaited European Chips Act. This daring legislative proposal aims to fortify Europe’s semiconductor supply chain and enhance its

Revolutionized Low-Code

You Should Use Low-Code Platforms for Apps

As the demand for rapid software development increases, low-code platforms have emerged as a popular choice among developers for their ability to build applications with minimal coding. These platforms not

Cybersecurity Strategy

Five Powerful Strategies to Bolster Your Cybersecurity

In today’s increasingly digital landscape, businesses of all sizes must prioritize cyber security measures to defend against potential dangers. Cyber security professionals suggest five simple technological strategies to help companies

Global Layoffs

Tech Layoffs Are Getting Worse Globally

Since the start of 2023, the global technology sector has experienced a significant rise in layoffs, with over 236,000 workers being let go by 1,019 tech firms, as per data

Huawei Electric Dazzle

Huawei Dazzles with Electric Vehicles and Wireless Earbuds

During a prominent unveiling event, Huawei, the Chinese telecommunications powerhouse, kept quiet about its enigmatic new 5G phone and alleged cutting-edge chip development. Instead, Huawei astounded the audience by presenting

Cybersecurity Banking Revolution

Digital Banking Needs Cybersecurity

The banking, financial, and insurance (BFSI) sectors are pioneers in digital transformation, using web applications and application programming interfaces (APIs) to provide seamless services to customers around the world. Rising

FinTech Leadership

Terry Clune’s Fintech Empire

Over the past 30 years, Terry Clune has built a remarkable business empire, with CluneTech at the helm. The CEO and Founder has successfully created eight fintech firms, attracting renowned

The Role Of AI Within A Web Design Agency?

In the digital age, the role of Artificial Intelligence (AI) in web design is rapidly evolving, transitioning from a futuristic concept to practical tools used in design, coding, content writing

Generative AI Revolution

Is Generative AI the Next Internet?

The increasing demand for Generative AI models has led to a surge in its adoption across diverse sectors, with healthcare, automotive, and financial services being among the top beneficiaries. These

Microsoft Laptop

The New Surface Laptop Studio 2 Is Nuts

The Surface Laptop Studio 2 is a dynamic and robust all-in-one laptop designed for creators and professionals alike. It features a 14.4″ touchscreen and a cutting-edge design that is over

5G Innovations

GPU-Accelerated 5G in Japan

NTT DOCOMO, a global telecommunications giant, is set to break new ground in the industry as it prepares to launch a GPU-accelerated 5G network in Japan. This innovative approach will

AI Ethics

AI Journalism: Balancing Integrity and Innovation

An op-ed, produced using Microsoft’s Bing Chat AI software, recently appeared in the St. Louis Post-Dispatch, discussing the potential concerns surrounding the employment of artificial intelligence (AI) in journalism. These

Savings Extravaganza

Big Deal Days Extravaganza

The highly awaited Big Deal Days event for October 2023 is nearly here, scheduled for the 10th and 11th. Similar to the previous year, this autumn sale has already created

Cisco Splunk Deal

Cisco Splunk Deal Sparks Tech Acquisition Frenzy

Cisco’s recent massive purchase of Splunk, an AI-powered cybersecurity firm, for $28 billion signals a potential boost in tech deals after a year of subdued mergers and acquisitions in the

Iran Drone Expansion

Iran’s Jet-Propelled Drone Reshapes Power Balance

Iran has recently unveiled a jet-propelled variant of its Shahed series drone, marking a significant advancement in the nation’s drone technology. The new drone is poised to reshape the regional

Solar Geoengineering

Did the Overshoot Commission Shoot Down Geoengineering?

The Overshoot Commission has recently released a comprehensive report that discusses the controversial topic of Solar Geoengineering, also known as Solar Radiation Modification (SRM). The Commission’s primary objective is to

Remote Learning

Revolutionizing Remote Learning for Success

School districts are preparing to reveal a substantial technological upgrade designed to significantly improve remote learning experiences for both educators and students amid the ongoing pandemic. This major investment, which

Revolutionary SABERS Transforming

SABERS Batteries Transforming Industries

Scientists John Connell and Yi Lin from NASA’s Solid-state Architecture Batteries for Enhanced Rechargeability and Safety (SABERS) project are working on experimental solid-state battery packs that could dramatically change the

Build a Website

How Much Does It Cost to Build a Website?

Are you wondering how much it costs to build a website? The approximated cost is based on several factors, including which add-ons and platforms you choose. For example, a self-hosted

Battery Investments

Battery Startups Attract Billion-Dollar Investments

In recent times, battery startups have experienced a significant boost in investments, with three businesses obtaining over $1 billion in funding within the last month. French company Verkor amassed $2.1

Copilot Revolution

Microsoft Copilot: A Suit of AI Features

Microsoft’s latest offering, Microsoft Copilot, aims to revolutionize the way we interact with technology. By integrating various AI capabilities, this all-in-one tool provides users with an improved experience that not

AI Girlfriend Craze

AI Girlfriend Craze Threatens Relationships

The surge in virtual AI girlfriends’ popularity is playing a role in the escalating issue of loneliness among young males, and this could have serious repercussions for America’s future. A

AIOps Innovations

Senser is Changing AIOps

Senser, an AIOps platform based in Tel Aviv, has introduced its groundbreaking AI-powered observability solution to support developers and operations teams in promptly pinpointing the root causes of service disruptions