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

Connascence of Meaning

DZone's Guide to

Connascence of Meaning

· DevOps Zone
Free Resource

The DevOps Zone is brought to you in partnership with Sonatype Nexus. The Nexus Suite helps scale your DevOps delivery with continuous component intelligence integrated into development tools, including Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube and more. Schedule a demo today

This is part 2 of a short series of posts in which I explore TDD using only connascence as my guide. In the previous article I wrote a test, made it pass, and then refactored away the strongest coupling. That coupling took the form of some Connascence of Value between the test and the Checkout. Later, after the excitement of publishing the post had died away, I realised there was still some non-trivial connascence in the code. Today it’s time to fix that.

Here is the test I finished with last time:

public class CheckoutTests {
 
  @Test
  public void basicPrices() {
    int price = randomPrice();
    Checkout checkout = new Checkout();
    checkout.scan("A", price);
    assertEquals(price, checkout.currentBalance());
  }
}

First, let’s review the connascence in this code. (And please let me know if I have missed any this time too!)

  1. Connascence of Name: The test knows the names of the methods to call on the checkout object. This is level 1 (of 9) on the connascence scale — the weakest and least damaging form of coupling.
  2. Connascence of Type: The test knows which class to instantiate, and it knows the types of the various method parameters. This is level 2 on the scale, and is thus also relatively benign.
  3. Connascence of Meaning: Both classes know that we are representing monetary values using ints, and products using strings. This is level 3 on the scale; while still relatively harmless, it is worth removing before the code gets too much bigger.

I think it is worth taking a moment to examine why I feel that this is Connascence of Meaning and not purely Connascence of Type. Yes, the test and the Checkout have to agree on the types of the parameters and return values, but I think there’s more: both also have to agree on what those values mean. Does that int represent pence, pounds, euros? Could that product string in future become a guid, or a barcode? The test and the Checkout are coupled by their shared knowledge of how those domain concepts (monetary values and product codes) are represented and interpreted. If I let that knowledge proliferate, it could become difficult to change either of them. I may incur subtle bugs if either is interpreted or represented in a different way somewhere in the application in future. This is the Ariane 5 bug in waiting.

(I clearly remember the pain of encountering a large .Net financial application in which monetary values were represented as ints, decimals or floats in various areas of the code. The compiler “helped” by casting between these representations, so the inevitable bugs were only discoverable at runtime.)

So although I have “only” Connascence of Meaning, I want to nip it in the bud early. The cost of doing that is low right now, and the cost of not doing it could escalate quickly later. So, on with the motley…

I’ll deal with the money first.

cov3

As before, I can weaken the connascence by bringing the “ends” of the coupling together in one place. But this time I cannot easily inject a value into the Checkout, because the problem here is one of types. So instead, I create a new type and hide the int inside it. Money will be the only class that knows how monetary values are stored:

cov4

The change to the test is simple:

public class CheckoutTests {
 
  @Test
  public void basicPrices() {
    Money price = randomPrice();
    Checkout checkout = new Checkout();
    checkout.scan("A", price);
    assertEquals(price, checkout.currentBalance());
  }
 
  private Money randomPrice() {
    int pence = new Random().nextInt(1000);
    return Money.fromPence(pence);
  }
}

Similarly the checkout now uses Money objects instead of ints:

public class Checkout {
  private Money balance;
 
  public Checkout scan(String sku, Money price) {
    balance = price;
  }
 
  public Money currentBalance() {
    return balance;
  }
}

And finally, here is the new Money class itself:

public class Money {
  private int pence;
 
  private Money(int pence) {
    this.pence = pence;
  }
 
  public static Money fromPence(int pence) {
    return new Money(pence);
  }
}

Note that I clearly document the meaning of the parameter to Money’s factory method, while the actual constructor is hidden from view. This gives me more control over how Money objects are created, and helps keep the internal representation private.

Next, I will move on to do something very similar with product codes. But before I do that I just want to expand on (ie. recycle) the current test so that it can cater for multiple items:

@Test
public void basicPrices() {
  Money priceOfA = randomPrice();
  Checkout checkout = new Checkout();
  Money priceOfB = randomPrice();
  checkout.scan("A", priceOfA).scan("B", priceOfB);
  assertEquals(priceOfA.add(priceOfB), checkout.currentBalance());
}

This simple change forces Money to acquire a wee bit more richness:

public class Money {
  public static final Money ZERO = new Money(0);
  private int pence;
 
  private Money(int pence) {
    this.pence = pence;
  }
 
  public static Money fromPence(int pence) {
    return new Money(pence);
  }
 
  public Money add(Money other) {
    return new Money(pence + other.pence);
  }
 
  @Override
  public boolean equals(Object other) {
    Money m = (Money) other;
    return pence == m.pence;
  }
 
  @Override
  public int hashCode() {
    return new Integer(pence).hashCode();
  }
}

I like this. By fixing some Connascence of Meaning I have “discovered” a domain concept, and it has quickly fleshed out with a perfectly sensible set of behaviours. This would be perfect if only Java allowed operator overloading…

Now I take a look at those strings that represent the products. Arguably the Connascence of Meaning here is less severe, because as yet the Checkout isn’t using the product code passed to it. I could leave this one until I have tests that force Checkout to check that string. I decide to do just that, because I can’t predict when that will be, or how the code will turn out in that uncertain future.

So, to summarise, I fixed Connascence of Meaning by hiding the choice of data representation for monetary values. If I later decide to switch to pounds with decimal places representing the pence, I only have to change the Money class. The rest of the application is isolated from such change.

This feels like a lot of work for just one test. Was it justified? Well, first I note that it is two tests, because I recycled one. Secondly, I have named a domain concept and the very simple test I have has already caused it to flesh out with some behaviours. I am happy with this code, and I am comfortable that it satisfies Extreme Normal Form. I also expect that some people will disagree.

In the next post I will add discounts and create some Connascence of Position…


The DevOps Zone is brought to you in partnership with Sonatype Nexus. Use the Nexus Suite to automate your software supply chain and ensure you're using the highest quality open source components at every step of the development lifecycle. Get Nexus today

Topics:

Published at DZone with permission of Kevin Rutherford, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

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

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

{{ parent.tldr }}

{{ parent.urlSource.name }}