Attributes, states and time

Reading time:

4–5 minutes

Classes, with their attributes and methods, unite two fundamental concepts of computer science: data and algorithms.

Some caution is advised when using attributes, as they assign a state to objects created based on their respective class, automatically binding them to the concept of time.

Understanding and modeling time, state, and state changes is not easy. Philosophers, physicists, and many other brilliant minds have grappled with this, yet the situation is often not as clear as one might hope.

Therefore, attributes should be used thoughtfully to avoid creating unnecessary problems.

It works without attributes

There’s nothing inherently wrong with having a class without attributes. Such a class doesn’t automatically become a utility class, and its methods aren’t automatically utility methods. It’s just a friendly helper class.

However, when a public method of this class is called, it must receive all necessary dependencies via input parameters and then execute its algorithms.

Such methods can be analyzed effectively, provided the recommendations from CleanABAP are followed. This is because the memory context is always local. You work with the data that a method can access thanks to its input and output parameters.

Some situations require attributes

On the other hand, some classes require attributes because, without them, data cannot be retained for the lifetime of the object. Here are a few examples:

  1. A specific property should be assigned to an object during its creation via the constructor or a factory method, and it should retain this property from then on.
  2. An object is sequentially supplied with data through several successive SET methods, where the order can be significant.
  3. The result of an algorithm within the class should be retained for the lifetime of the object.

Points 1) and 2) are particularly important in the context of dependency injection.

Attributes create dependencies

As soon as you assign attributes to a class, you inevitably become bound to state, state changes, and time. This raises questions such as:

  1. Who is allowed to change a state?
  2. Which states are considered valid?
  3. In what temporal sequence certain states may occur?
  4. Which operations are permitted in which state.

In short: from here on, things get complicated and demanding.

To address this, there are several approaches. Here are five examples:

  1. Fail fast, fail early” is a common approach to avoid continuing to work with an invalid state.
  2. Immutability simplifies state changes in terms of timing and nature, because the state can only be assigned when the object is created. After that, it can no longer be changed. If you want a new state, you consequently get a new object, and the old object becomes obsolete. Drastic, but effective.
  3. Encapsulation through visibility helps define who is allowed to change the attributes and thus the state of an object. Ideally, only the object itself can do this, but in practice, this is often enough to put itself into an invalid state. The friends concept or public attributes that are not read-only even offer the possibility of creating an invalid state from outside the class.
  4. Command-query separation is a helpful principle for dividing methods into two categories. Command methods execute algorithms that can modify the state of an object. Query methods, on the other hand, return data and therefore do not modify the object’s state.
  5. Common sense, combined with some experience. If necessary, you can borrow some from colleagues. The fundamental question is always how much the complexity of a class increases with an extra attribute and how much effort is required to manage that increased complexity.

What you shouldn’t do

Whether attributes are needed depends on the required design. What you should definitely avoid is unnecessarily giving attributes to a class. The reason for this is usually to leverage the global visibility of attributes within the class, allowing easy access to a global context from any method.

What initially seems like a quick win turns out to be a real problem. Since every method can access and modify the attributes, every method is initially suspected of doing just that. Therefore, anyone wanting to understand the code faces the challenge of considering both the local and global context.

Depending on the size of the class and the number of attributes, this can be very exhausting. If this sounds familiar, we’ve seen this exact situation thousands of times with top-level includes in function groups in procedural programming — a bad legacy from the past. Fortunately, we don’t have to repeat the past.

Summary

Here are the key takeaways from this article:

  1. A class can have attributes, but it doesn’t have to. This doesn’t change its nature.
  2. If attributes are needed, their number should be small, e.g., 3–5 attributes.
  3. Every attribute needs a clearly defined, business or technical related justification.
  4. The class source code should clearly explain when and why an attribute is accessed.
  5. Once an attribute is introduced, the topics of state, state changes, and time become relevant. This significantly complicates matters.

Thank you for reading. If you enjoyed the post, please share the article with your community. Thanks in advance.

Michael (a mind forever voyaging)