Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Temporal Coupling Between Method Calls

DZone's Guide to

Temporal Coupling Between Method Calls

Sequential method calls feature temporal coupling when these calls have to remain in a set order. Unfortunately, there can be some negative effect, but we can clear those up by simply turning static procedures into functions.

· Integration Zone
Free Resource

Discover how Microservices are a type of software architecture where large applications are made up of small, self-contained units working together through APIs that are not dependent on a specific language. Brought to you in partnership with AppDynamics.

Temporal coupling happens between sequential method calls when they must stay in a particular order. This is inevitable in imperative programming, but we can reduce the negative effect of it just by turning those static procedures into functions. Take a look at this example:

class Foo {
  public List<String> names() {
    List<String> list = new LinkedList();
    Foo.append(list, "Jeff");
    Foo.append(list, "Walter");
    return list;
  }
  private static void append(
    List<String> list, String item) {
    list.add(item.toLowerCase());
  }
}

What do you think about that? I believe it's clear what names() is doing — creating a list of names. In order to avoid duplication, there is a supplementary procedure, append(), which converts an item to lowercase and adds it to the list.

This is poor design.

It is a procedural design, and there is temporal coupling between lines in method names().

Let me first show you a better (though not the best!) design:

class Foo {
  public List<String> names() {
    return Foo.with(
      Foo.with(
        new LinkedList(),
        "Jeff"
      ),
      "Walter"
    );
  }
  private static List<String> with(
    List<String> list, String item) {
    list.add(item.toLowerCase());
    return list;
  }
}

An ideal design for method with() would create a new instance of List, populate it through addAll(list), then add(item) to it, and finally return. That would be perfectly immutable, but slow.

So, what is wrong with this:

List<String> list = new LinkedList();
Foo.append(list, "Jeff");
Foo.append(list, "Walter");
return list;

It looks perfectly clean, doesn't it? Instantiate a list, append two items to it, and return it. Yes, it is clean — for now. Because we remember what append() is doing. In a few months, we'll get back to this code, and it will look like this:

List<String> list = new LinkedList();
// 10 more lines here
Foo.append(list, "Jeff");
Foo.append(list, "Walter");
// 10 more lines here
return list;

Is it so clear now that append() is actually adding "Jeff" to list? What will happen if I remove that line? Will it affect the result being returned in the last line? I don't know. I need to check the body of method append() to make sure.

Also, how about returning list first and calling append() afterwards? This is what possible "refactoring" may do to our code:

List<String> list = new LinkedList();
if (/* something */) {
  return list;
}
// 10 more lines here
Foo.append(list, "Walter");
Foo.append(list, "Jeff");
// 10 more lines here
return list;

First of all, we return list too early, when it is not ready. But did anyone tell me that these two calls to append() must happen before return list? Second, we changed the order of append() calls. Again, did anyone tell me that it's important to call them in that particular order?

Nobody. Nowhere. This is called temporal coupling.

Our lines are coupled together. They must stay in this particular order, but the knowledge about that order is hidden. It's easy to destroy the order, and our compiler won't be able to catch us.

To the contrary, this design doesn't have any "order":

return Foo.with(
  Foo.with(
    new LinkedList(),
    "Jeff"
  ),
  "Walter"
);

It just returns a list, which is constructed by a few calls to the with() method. It is a single line instead of four.

As discussed before, an ideal method in OOP must have just a single statement, and this statement is return.

The same is true about validation. For example, this code is bad:

list.add("Jeff");
Foo.checkIfListStillHasSpace(list);
list.add("Walter");

While this one is much better:

list.add("Jeff");
Foo.withEnoughSpace(list).add("Walter");

See the difference?

And, of course, an ideal approach would be to use composable decorators instead of these ugly static methods. But if it's not possible for some reason, just don't make those static methods look like procedures. Make sure they always return results, which become arguments to further calls.

Discover the six challenges and best practices in managing microservice performance, brought to you in partnership with AppDynamics.

Topics:
java ,temporal ,coupling

Published at DZone with permission of Yegor Bugayenko, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}