Expose Cause in Variable

 class​ TransmissionParser {
 static​ Transmission parse(String rawMessage) {
 if​ (rawMessage != ​null
  && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
»throw​ ​new​ IllegalArgumentException(
  String.format(​"Expected %d, but got %d characters in '%s'"​,
  Transmission.MESSAGE_LENGTH, rawMessage.length(),
  rawMessage));
  }
 
  String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
  String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
 try​ {
 int​ id = Integer.parseInt(rawId);
  String content = rawContent.trim();
 return​ ​new​ Transmission(id, content);
  } ​catch​ (NumberFormatException e) {
»throw​ ​new​ IllegalArgumentException(
  String.format(​"Expected number, but got '%s' in '%s'"​,
  rawId, rawMessage), e);
  }
  }
 }

We sometimes forget that exceptions are also simply classes that can have their own fields, methods, and constructors—not just the standard ones. And we can use them to expose the cause of an exception in a machine-readable way.

In the code above, you can spot two issues: there’s duplicate code and concealed information.

The code duplication is where we add rawMessage to the message of the IllegalArgumentException twice in the same way: " in ’%s’". This can quickly get inconsistent the larger the software grows.

Concealed information is even more problematic. We encode the rawMessage within the message of the exception. This element can’t be extracted easily later on when, for instance, we want to inform the end user of our software what kind of message caused an error. It’s concealed from any further processing. And we can also easily forget to add it to the exception message.

So how can we make the context of the exception easier to inspect?

 class​ TransmissionParser {
 static​ Transmission parse(String rawMessage) {
 if​ (rawMessage != ​null
  && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
»throw​ ​new​ MalformedMessageException(
  String.format(​"Expected %d, but got %d characters"​,
  Transmission.MESSAGE_LENGTH, rawMessage.length()),
  rawMessage);
  }
 
  String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
  String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
 try​ {
 int​ id = Integer.parseInt(rawId);
  String content = rawContent.trim();
 return​ ​new​ Transmission(id, content);
  } ​catch​ (NumberFormatException e) {
»throw​ ​new​ MalformedMessageException(
  String.format(​"Expected number, but got '%s'"​, rawId),
  rawMessage, e);
  }
  }
 }
 final​ ​class​ MalformedMessageException ​extends​ IllegalArgumentException {
»final​ String raw;
 
  MalformedMessageException(String message, String raw) {
 super​(String.format(​"%s in '%s'"​, message, raw));
 this​.raw = raw;
  }
  MalformedMessageException(String message, String raw, Throwable cause) {
 super​(String.format(​"%s in '%s'"​, message, raw), cause);
 this​.raw = raw;
  }
 }

We suggest that you define and use a custom exception: the MalformedMessageException with its own raw message field. We can obtain that field later on for more detailed end-user information or for a more thorough handling of that exception.

The message still adheres to the template introduced in Explain Cause in Message—the constructor simply appends the raw message. That makes it easier to have a consistent exception message. If we extract the message construction into a separate method, we also get rid of code duplication.

You just need to make sure that your custom exceptions stay immutable, which we do by declaring the class and its fields final.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.50.222