Turn Your Spaghetti Code into Functions - Part 2

How Predicates can help clean your code, and how to start using them

Read Part 1, first.

In Part 1, we started with an example of common business logic code, and an analogy based on cramming an elephant into a Smart Car. We did some refactoring to untangle the nested if/else blocks, but we left after we finished cramming the elephant into a Smart Car.

In many ways, it feels "good enough", but what if I told you we can get it better? Java 8 brings us a new tool to contain and use the logic within an if statement - the Predicate. In terms of elephants driving cars, we can get it driving a stylish convertible.

So you have a lot of conditional logic, and you find yourself copy-pasting conditions from one logic block to the other. It's easy, it's seductive, but it's wrong. Copy pasting is error prone and extra work! If you're like me, you try to work as little as possible - that's the computer's job. Java 8 provides a new tool to prevent copy-pasting and keep your code DRY.

Using Predicates allows us to encapsulate logic as a variable. This makes for two features that are particularly powerful

  1. Variable names communicate the intent of the logic
  2. Logic is resusable and individually testable

And Now, With Predicates

Before Java 8, I wasn't tuned in to the functional programming world. The last time I remember hearing about Predicates was in school when I was ignoring a grammar lesson. It turns out that, when programming, Predicates can really improve your code.

A grammar school

Here we will make the if/else blocks more readable by creating Predicates out of the logic they represent. Predicates can be named, which allows developers to name rules in a way that allows for clear discussions with even non-technical users.

static final Predicate<WidgetTransfer> suffientAmount = trans -> trans.getTransferer().getAccount(trans.getFromAccount()).getBalance().compareTo(trans.getAmount()) > 0;
static final Predicate<String> isPartner = ttc -> ttc.equals("200");
static final Predicate<String> isFriendsAndFamily = ttc -> ttc.equals("710");
static final Predicate<String> isFriendAndFamilyDiscountLegal = ac -> ac.matches("574|213|363|510");
static final Predicate<String> isPartneringArea = ac -> ac.matches("907|412|213");
static final Predicate<String> isDirigibleArea = ac -> ac.matches("213");
static final Predicate<String> isDirigibleCategory = cat -> cat.equals("D");
static final Predicate<String> isInternal = tc -> tc.equals("I");

public static final String checkWidgetTransfer(WidgetTransfer transfer) {
    String businessRuleErrors = "";

    String transferTypeCode = transfer.getTransferTypeCode();
    String areaCode = transfer.getAreaCode();
    String category = transfer.getTransferer().getCategory();
    String typeCode = transfer.getTypeCode();

    if (suffientAmount.test(transfer)) {
        businessRuleErrors += "Insufficient balance to transfer ; ";
    }

    if (isPartner.test(transferTypeCode)
            && isPartneringArea.negate().test(areaCode)) {
        businessRuleErrors += "This area is not a transfer eligible area. ; ";
    }

    if (isPartner.test(transferTypeCode)
            && isDirigibleArea.test(areaCode)
            && isDirigibleCategory.test(category)) {
        businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
    }

    if (isFriendsAndFamily.test(transferTypeCode)
            && isFriendAndFamilyDiscountLegal.negate().test(areaCode)) {
        businessRuleErrors += "This area is not an eligible area. ; ";

    }

    if (isInternal.negate().test(typeCode)
            && !isBlockSize(transfer)) {
        businessRuleErrors += "Amount is too small for I type transfer. ; ";
    }

    if (isInternal.negate().test(typeCode)
            && isTotalOverCap(transfer)) {
        businessRuleErrors += "This transfer is too large. ; ";
    }

    return businessRuleErrors;
}

The Good

  • Each if block is readable in something approximating "business English"
  • The rules are defined as Predicates
  • The rules are portable and reusable.
  • The rules are also individually testable, without having to test each branch at once

The Bad

  • This technique still uses && which is not idiomatic with functions in Java
  • We are forced to use && because the Predicates take different types of objects, so we can't chain them together
  • While the Predicates that make up the rules are portable, the rules themselves are made of multiple Predicates and are not portable
  • Nothing has been done that can't be done with ordinary methods
  • Good old public boolean isSufficientAmount(String amount) would suffice
  • We still have to create all these property variables to get the appropriate values to give to our Predicates

Predicate Chaining

Let's fix some of the stuff on the 'bad' list from the previous example.

We can get rid of && by using just a little bit more of the functional interface and refactoring the Predicates to all take the same type of object, in this case a WidgetTransfer object. The goal is to make our Predicates like legos - interlocking and interchangeable.

lego elephant

static final Predicate<WidgetTransfer> suffientAmount = trans -> trans.getTransferer().getAccount(trans.getFromAccount()).getBalance().compareTo(trans.getAmount()) > 0;
static final Predicate<WidgetTransfer> isPartner = trans -> trans.getTransferTypeCode().equals("200");
static final Predicate<WidgetTransfer> isFriendsAndFamily = trans -> trans.getTransferTypeCode().equals("710");
static final Predicate<WidgetTransfer> isFriendAndFamilyDiscountLegal = trans -> trans.getAreaCode().matches("574|213|363|510");
static final Predicate<WidgetTransfer> isPartneringArea = trans -> trans.getAreaCode().matches("907|412|213");
static final Predicate<WidgetTransfer> isDirigibleArea = trans -> trans.getAreaCode().matches("213");
static final Predicate<WidgetTransfer> isDirigibleCategory = trans -> trans.getTransferer().getCategory().equals("D");
static final Predicate<WidgetTransfer> isInternal = trans -> trans.getTypeCode().equals("I");
static final Predicate<WidgetTransfer> isBlockSize = trans -> isBlockSize(trans);
static final Predicate<WidgetTransfer> isTotalOverCap = trans -> isTotalOverCap(trans);

public static final String checkWidgetTransfer(WidgetTransfer transfer) {
    String businessRuleErrors = "";

    if (suffientAmount.test(transfer)) {
        businessRuleErrors += "Insufficient balance to transfer ; ";
    }

    if (isPartner.and(isPartneringArea.negate()).test(transfer)) {
        businessRuleErrors += "This area is not a transfer eligible area. ; ";
    }

    if (isPartner.and(isDirigibleArea).and(isDirigibleCategory).test(transfer)) {
        businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
    }

    if (isFriendsAndFamily.and(isFriendAndFamilyDiscountLegal.negate()).test(transfer)) {
        businessRuleErrors += "This area is not an eligible area. ; ";
    }

    if (isInternal.negate().and(isBlockSize.negate()).test(transfer)) {
        businessRuleErrors += "Amount is too small for I type transfer. ; ";
    }

    if (isInternal.negate().and(isTotalOverCap.negate()).test(transfer)) {
        businessRuleErrors += "This transfer is too large. ; ";
    }

    return businessRuleErrors;
}

The Good

  • We get rid of extra variables that hold string values from the WidgetTransfer object
  • We compact our if-blocks while retaining readability
  • We only evaluate one type of object

The Bad

There is very little bad about this refactor point. The conditionals are all very easy to read. It's clear what each rule is, and what branch. If I didn't know what I have planned for the next article, I would be satisfied to stop here.

Next Steps

elephant driving a convertible citroen

All our rules are Predicates and each rule takes the same kind of object, a WidgetTransfer. That makes our rule composable in the fashion demonstrated above, but there are some improvements we can make to how we compose the business rules.

The first improvement is to combine small rules into larger rules - we are doing this in conditional statements, but it is just as easy to do so in a Predicate. We can also create a Validator object to create a collection of business rules and error messages. A Validator dispenses with the need to create a complex nested if/else logic structure, and is a concrete unit of business logic that can be shared, re-used and tested.

Sign up for my email list to get notifcations about updates in continuing series, and a monthly digest of interesting programming and tech leadership articles

We will go over using Validators in the to-be-written Turn Your Spaghetti Code into Functions, Part III

Credits

Thank you roebot for the left facing Smart Car

Thank you Oliver Dodd for the elephant

Thank you Phillip Pessar for the convertible

Thank you JPL and NASA/Space Telescope Science Institute for the edge-on galaxy picture

Thank you Philip Sheldrak for the grammar school

Tags: java technique diy how-to tutorial guide lambda function refactor

Get great articles about development every month here ⬇️

3 Phases for Building a Culture of Learning


I attended Joseph Matsey's talk on building a culture of learning at Abstraction's last year. I really enjoyed the talk, and have been thinking about and implementing ideas from it over the past year. Here's a summary of my takeaway for the 3 different phases of culture building (with a bonus 4th phase, of "you have a culture")

Guerilla Learning

This is where a lot of places start - there isn't really any particular culture of learning, but some learning exercises and activities are supported

  • High resistance to taking time from shipping
  • Ad hoc learning
  • Onboarding by oral tradition
  • Few folks driving learning
  • Approach
    • Think opportunistically
    • Improve team members primary roles
    • Seek fast returns with low investment
    • Examples
      • Workshops
      • Meetups
      • Build a team library
      • New hire buddies
        • Try cross-team buddies
    • Dangers
      • Buy in never starts
      • Stay stuck in guerilla learning forever

Buy in Starts

This is a dangerous intermediate phase, where some culture building has happened, but it's not baked in or fully supported by management

  • New hires have some kind of organized learning
  • Team members want more, but aren't empowered
  • Approach
    • Leverage internal experts (workshops, talks)
    • Seed good role models (and shame everyone else)
    • Mentor mentors (mentorship is an orthogonal skillset to what you do at work)
    • Examples
      • Weekly talks, 4 15 minute talks
      • Project updates
      • Tech approaches
      • Senior leadership talks
      • Ops talks
    • Dangers
      • Key folks leaving
      • "Takes too much time" payout not obvious, track and figure out how to measure returns
      • Mentor burnout

Making it Stick

This is the final phase, that takes management buy in, re-programming existing employees and enculturating new hires

  • Boringly normal
    • From "workshops are amazing" to "oh god a workshop"
  • Approach
    • Momentum to sustain effort
    • Sustained learning
    • Make people forget the old way
    • Examples
      • Learning/teaching as a KIP
      • Apprenticeships
      • 3 part onboarding
        • Teach skills
        • Teach processes, history and context
        • Personal development
      • 20% time
    • Dangers
      • Payoffs hard to measure
      • No one is responsible
      • Losing the habit

Self Sustaining

At some critical mass point, it continues lurching forward. This point is when you have a durable culture of learning

Credits

Thank you Mat the W for the photo of Hammerschlag

Tags: leadership culture learning notes talks conferences

Get great articles about development every month here ⬇️

Turn Your Spaghetti Code into Functions - Part 1


Developers can sink a lot of hours into fighting business rule code. Spaghetti business rules make it so little changes need to be copy pasted repeatedly throughout if/else blocks. It's like trying to shoehorn an elephant into a Smart Car, when it should be like snapping together legos.

Anyone who has worked on a 'mature' set of business rules knows that untangling what is going on is extremely hard. If cramming the elephant into the Smart Car was tough, getting it out is an order of magnitude tougher. I'm going to show how to make your Smart Car-driving elephant a little happier.

Business rules are your money-makers and form the essence of your business's being. They also change a lot. This dynamic often leads to rushed work in business rules and over the long run creates a nasty mess right in the heart of your money maker. A nasty mess is hard to read and even harder to figure out what it is supposed to be doing.

Further, the OO way of encapsulating business rules is either fundamentally inadequate, or so widely misunderstood by working developers that the common pattern is a god class that mutates everything it touches. Testing a huge class, stuffed with spaghetti and mutators is daunting, at best, impossible at worst. No tests make it very hard to develop without fear.

Here's a set of techniques for making the code easier to follow, so you can put your effort into understanding your stakeholders and making sure what's in the code is what they want to be in the code.

This will teach you how to untangle business rules, so you can easily work with them.

Readability

Deep nesting impairs readability. Even when developers use tabs well and are consistent with their curly brace usage, around the 3rd layer of if/else statements, it starts getting hard to tell what particular branch you are reading.

Is this the one with the null child object? I'll just check for null again here really quick

Avoid this kind of impulse by structuring your code so there is never any doubt about what you are checking.

Long conditions that reach deep into child or grandchild objects also impair readability. Stakeholders never say (unless they've been terribly abused) something like:

If the transfer type code is 200 and it's not in areas 907, 412 or 213, then it's not allowed

  1. That's got more "nots" than a human likes to say
  2. Humans don't usually go on about codes.

They might say something like:

If it's a partner transaction, we only allow it in partner areas

If your code takes mental backflips to match how your stakeholders talk, you're gonna have a bad time. So are they, and they won't even know why.

Testability

Gnarly business rule code is hard to test. The twists and turns of deeply nested if/else statements are easy to get lost in, easy to forget a branch and sometimes just plain impossible to set up in a testing harness. No tests mean every change is dangerous - you are one misplaced }, or backwards > from giving your users a headache.

Gnarly business rule code seems to encourage devs to mutate the objects it is validating. I don't know why this is, but I've seen it many times in the wild. State mutation amps up the testing difficulty by an order of magnitude. Now, not only must you test for messages and expected errors, but you must detect changes to the object you are validating. Are they intentional? Are they correct?

Who knows? I promise no one wrote it down.

Response speed

Stakeholders, users, managers and developers constantly engage in timeline tug of war. Technical debt hamstrings developers' ability to give their users what they need. A good 'tell' that your system has a lot of technical debt is users can easily describe a feature, but developers can't easily implement it. The simplest things turn into month long bushwhacking exercises.

To me, nothing says bushwhacking like coming down a mountain, getting off trail and fighting through Devil's Club and loose scree -

Wait. I meant nothing says bushwhacking like coming into a method and getting on the wrong nested if/else branch and fixing something that wasn't broken in the first place. Or noticing that 100 lines of code have never, ever been executed, because they wouldn't work. Or any number of the fun surprises that spaghetti-fied business rules bring.

Composability

Composability lets you create new rules out of old rules. Most new business rules have a lot in common with existing rules. The ability to combine existing rules and add that one special case is extremely powerful. Your stakeholders will be amazed at your turnaround time!

The spaghetti style is anti-composable. Copy-pasting, duplicating code and mangling switch statements are practically requirements with spaghetti-ed business rules. Good luck re-using something to get at the one

Example Bad Code

Here's an example of standard Java business logic that evaluates a business object (BusinessTransfer) and creates messages to return to the user if it violates business rules:

public static final String checkWidgetTransfer(WidgetTransfer transfer) {
    String businessRuleErrors = "";

    if (transfer.getTransferer().getAccount(transfer.getFromAccount()).getBalance().compareTo(transfer.getAmount()) < 0) {
        businessRuleErrors += "Insufficient balance to transfer ; ";
    }


    if (transfer.getTransferTypeCode().equals("200")) {
        if (!transfer.getAreaCode().matches("907|412|213")) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        } else if (!transfer.getAreaCode().matches("213")) {
            if (transfer.getTransferer().getCategory().equals("D")) {
                businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
            }
        }
    } else if (transfer.getTransferTypeCode().equals("710")) {
        if (!transfer.getAreaCode().matches("574|213|363|510")) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        }
    }


    if (transfer.getTypeCode().equals("I")) {
        if (isBlockSize(transfer)) {
            businessRuleErrors += "Amount is too small for I type transfer. ; ";
        }
        if (isTotalOverCap(transfer)) {
            businessRuleErrors += "This transfer is too large. ; ";
        }
    }

    return businessRuleErrors;
}


public static boolean isBlockSize(WidgetTransfer transfer) {
    return transfer.getAmount().compareTo(1000) < 0;
}


public static boolean isTotalOverCap(WidgetTransfer transfer) {
    return transfer.getAmount().compareTo(1000000) > 0;
}

The above example is inspired by actual code running in the wild. What I show here is simplified, and anonymized. It's as hard to read as the original.

Reading it takes knowledge of what a "200" transfer type code is or what is acceptable data for different transfer area codes. The parentheses are nested, which makes copy-pasting (a common technique for working with this style of code) perilous. A dev can't afford to miss a single curly bracket without causing a hard-to-debug problem.

Logic Block Paradigm Shift

A quick way of refactoring long branching if/else code is to dispense with branches and with elses. By rephrasing each business rule into a positive constraint, a developer can check to see if the constraint conditions are met, rather than walking a branching logic tree. This technique increases line count a little, but improves readability a lot.

Another low hanging fruit is to create instance variables to hold values, rather than use getters (or worse, nested getters!). This gives the ability to name what a thing is in the context you are using it in, rather than relying on getters to have a good name in your context.

public static final String checkWidgetTransfer(WidgetTransfer transfer ) {
    String businessRuleErrors = "";
    Integer balance = transfer.getTransferer().getAccount(transfer.getFromAccount()).getBalance();
    Integer transferAmount = transfer.getAmount();
    String transferTypeCode = transfer.getTransferTypeCode();
    String areaCode = transfer.getAreaCode();
    String category = transfer.getTransferer().getCategory();
    String typeCode = transfer.getTypeCode();


    if (balance.compareTo(transferAmount) > 0) {
        businessRuleErrors += "Insufficient balance to transfer ; ";
    }


    {
        if (transferTypeCode.equals("200")
                && !areaCode.matches("907|412|213")) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        }
    }


    if (transferTypeCode.equals("200")
            && areaCode.matches("213")
            && category.equals("D")) {
        businessRuleErrors += "D Category Transferer can only be transferred in transfer area 213. ; ";
    }


    if (transferTypeCode.equals("710")
            && !areaCode.matches("574|213|363|510")) {
        businessRuleErrors += "This area is not an eligible area. ; ";


    }


    if (!typeCode.equals("I")
            && !isBlockSize(transfer)) {
        businessRuleErrors += "Amount is too small for I type transfer. ; ";
    }


    if (!typeCode.equals("I")
            && isTotalOverCap(transfer)) {
        businessRuleErrors += "This transfer is too large. ; ";
    }

    return businessRuleErrors;
}

The Good

  • The above code is much more readable.
    • Each business rule is contained in its own block of logic
    • All the properties needed to determine whether a condition has been met are named.
    • You can describe each business rule as written and it very nearly sounds like English.
  • The logic blocks are small and discrete.
    • There are no nested curly braces, so you'll have a hard time getting lost in the code.

The Bad

  • Business knowledge of what codes mean is still required
    • What is a "200" transfer type code? The code doesn't say, so hopefully it is documented somewhere...
  • Negative conditionals abound - if (!typeCode.equals("I"))
    • Negative conditionals are a little hard to say, and are harder to reason with than positive conditionals.
  • Still mutating those businessErrorMessages
    • This is just one more thing to set up in a unit test

What's Next?

I'll continue this refactoring discussion in a couple future posts. Check out my example refactoring repository for a preview!

We're going to see:

  • How to use Predicates to make your life better
  • Using actual objects, and not just their properties, to make your life better
  • Using Functions and validator objects, to make your life better

We'll try to get that elephant in his Smart Car, but mostly we'll try to make your life better.

Credits

Thank you Volantra for the right facing Smart Car Pic

Thank you roebot for the left facing Smart Car

Thank you Oliver Dodd for the elephant

Thank you Neils Heidenreich for the mutant strawberry

Thank you Peter Stevens for the Devil's Club

Thank you to Erica Schoonmaker for the picture of the books

Tags: java technique diy how-to tutorial guide lambda function refactor

Get great articles about development every month here ⬇️

Footnotes!


I was reading away on the internet when I saw a cool thing. Footnotes that popup when you hover over the little 1 note.

Lukas Mathis generously offered his code to the public, so I borrowed the code in his bookmarklet for Daring Fireball. I wasn't quite doing Gruber-style footnotes, which the code assumes.

Gruber Style Footnotes

A Gruber-style footnote has two parts:

  1. The superscript link
  2. The footnote with a return link

The superscript link, in Markdown, looks like this: <sup id="fnr-footnotes-1">[1](fn-footnotes-1)</sup>. The important parts are that the <sup> element has an id that starts with "fnr" (for "footnote return", I assume), and a link to the footnote that starts with "fn".

The footnote with a return link looks like: <a id="fn-footnotes-1">1: </a> This is an example footnote here - see how it has a return link [⏎](#fnr-footnotes-1). The important parts are the link with an id that starts with "fn" and the return link to the superscript link's <sup> element.

The script I borrowed assumes you have both parts of the Gruber-style footnote, with the appropriate prefixes and return links.

I adjusted the script's color a little to match my theme, but here it is, more or less unchanged from Lukas Mathis' original:

$(document).ready(function() {
  var sups = document.getElementsByTagName("sup");
  var footnotehtml = [];
  for (var i = 0; i < sups.length; i++) {
    var sup = sups[i];
    if (sup["id"] && sup["id"].substr(0, 3) == "fnr") {

      var footnr = sup["id"].substr(3);
      console.log(footnr);
      var footnote = document.getElementById("fn" + footnr);
      console.log(footnote);
      if (!footnote) continue;
      console.log("asdfasdfaf");
      footnotehtml[i] = footnote.parentNode.innerHTML;
      console.log(sup);
      sup.setAttribute("footnoteindex", i);
      sup.onmouseover = function(event) {
        var footnotepopup = document.getElementById("footnotepopup");
        if (footnotepopup) footnotepopup.parentNode.removeChild(footnotepopup);
        var index = parseInt(this.getAttribute("footnoteindex"));
        var popup = document.createElement("div");
        popup.innerHTML = footnotehtml[index];
        popup.id = "footnotepopup";
        popup.style.position = "absolute";
        popup.style.left = event.pageX - 125 + "px";
        popup.style.top = event.pageY + 25 + "px";
        popup.style.width = "15em";
        popup.style.textAlign = "left";
        popup.style.backgroundColor = "Gainsboro";
        popup.style.border = ".1em solid black";
        popup.style.borderRadius = "6px";
        popup.style.padding = "1em";
        document.body.appendChild(popup);
      };
      sup.onmouseout = function(event) {
        var footnotepopup = document.getElementById("footnotepopup");
        if (footnotepopup) footnotepopup.parentNode.removeChild(footnotepopup);
      };
    }
  }
});

Footnotes

1: This is an example footnote here - see how it has a return link

Credits

Thank you to Erica Schoonmaker for the picture of the books

Tags: javascript design insidebaseball

Get great articles about development every month here ⬇️

Changing an Uncontrolled Input to Controlled Error in React


React wants form inputs and the like to keep their state inside React, inside of inside the input. This style is called a "controlled input". They also offer "uncontrolled input" where the input manages its own state.

React apparently does not like inputs switching from controlled to uncontrolled. I was developing a form with the docs open next to my editor and I kept getting a Warning: CustomInput is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info:https://fb.me/react-controlled-components

As far as I could tell, my input was set up to always be controlled. I changed javascript around and googled for quite some time before realization set in

I was Switching a Form Value from '' to null

This is apparently a no-no in React's controlled component land. My initial state set up had all empty strings, and the only subsequent state manipulation was to get data from webservices. The webservices I'm working with return a fairly faithful representation of data from a database - nulls and all.

I solved it by converting my state variable to const in the render method - with a twist:

const nullable = this.state.nullable == null ? '' : this.state.nullable;

A little sprinkling of last minute converters and all is well.

Thank you Sebastian Dooris for the sprinkles

Tags: react newb learning

Get great articles about development every month here ⬇️

Finding Null or Empty String Checks in Java


I have a lot of code like this:

if( null == myString || "".equals(myString))
    doAThing();

The codes fine, really. Very idiomatic, gets the job done.

Lately, I've been on a predicate kick. I like replacing my conditionals with predicates that describe the intent of the conditional statement. I can also replace a lot of my boilerplate code with a couple common predicates. This is one I use a lot:

 public static final Predicate<String> NULL_OR_EMPTY = (in) -> null==in || "".equals(in);

It's true; if the code base had been crafted with Optionals or other null-avoidance techniques, that I could mostly ignore this check. My code base has history and weight and external interfaces, so there are plenty of this check and others like it.

I have been using this regex to find likely places that I can apply my predicate:

  /\(\s*null\s*==\s*(\S*)\s*\|\|\s*""\.equals\(\s*\1\s*\)\s*\)|\(\s*(\S*)\s*==\s*null\s*\|\|\s*""\.equals\(\s*\2\s*\)\s*\)|\(\s*null\s*==\s*(\S*)\s*\|\|\s*\3\.equals\(\s*""\s*\)\s*\)|\(\s*(\S*)\s*==\s*null\s*\|\|\s*\4\.equals\(\s*""\s*\)\s*\)|\(\s*""\.equals\(\s*(\S*)\s*\)\s*\|\|\s*null\s*==\s*\5\s*\)|\(\s*(\S*)\.equals\(\s*""\s*\)\s*\|\|\s*null\s*==\s*\6\s*\)|\(\s*""\.equals\(\s*(\S*)\s*\)\s*\|\|\s\7\s*==\s*null\s*\)|\(\s*(\S*).equals\(\s*""\s*\)\s*\|\|\s*\8\s*==\s*null\s*\)|\(\s*null\s*==\s*(\S*)\s*\|\|\s*""\s*==\s*\9\s*\)|\(\s*(\S*)\s*==\s*null\s*\|\|\s*""\s*==\s*\10\s*\)|\(\s*null\s*==\s*(\S*)\s*\|\|\s*\11\s*==\s*""\s*\)|\(\s*(\S*)\s*==\s*null\s*\|\|\s*\12\s*==\s*""\s*\)|\(\s*""\s*==\s*(\S*)\s*\|\|\s*null\s*==\s*\13\s*\)|\(\s*(\S*)\s*==\s*""\s*\|\|\s*null\s*==\s*\14\s*\)|\(\s*""\s*==\s*(\S*)\s*\|\|\s*\15\s*==\s*null\s*\)|\(\s*(\S*)\s*==\s*""\s*\|\|\s*\16\s*==\s*null\s*\)/g

It's an ugly one, but it matches a lot of common patterns for checking if a string is null or empty. I'm hopeful that folks will suggest either a better way of finding null or empty checks or help me add to my hideous regex!

Tags: java praxis practices java8 functions regex

Get great articles about development every month here ⬇️

How to Setup Cucumber Junit Tests for Java


cucumber growing

Java cucumber tests have 3 parts.

  1. The feature file
  2. The step definitions (the 'glue' file(s))
  3. The junit test file

The feature file is where the behavior, or business, oriented description of the test goes. These files are written with Gherkin syntax.

When writing Cucumber tests, write the feature files first. Have stakeholders, business owners and users write them, if that is possible in your organization. If not, the lead developer and/or project manager should write the feature files with the stakeholders, business owners and users. Put the work in and collect their stories and scenarios.

After the feature file is written, write the step definitions. This is what the Cucumber people call "glue" code. It's essentially regexes, setup and test code.

Finally, to make running the Cucumber tests easy in Java, write a junit wrapper class. This is an empty, annotated class that will enable Cucumber to use junit's test machinery to run Cucumber tests.

Example

I've created an example project. Cucumber uses Maven, so we have a project with a standard Maven directory structure:

+---src
|   +---main
|   |   +---java
|   \---test
|       +---java
|       \---resources

Organization

Let's put the feature files under test/resources. Let's put the junit classes under the principal test package - in this case com.gunnargissel.cucumberexample.artfight. Let's put the step definitions (the 'glue') in a stepdefs package under the principal test package - com.gunnargissel.cucumberexample.artfight.stepdefs

Behavior Scenario

For this example, we are creating the business engine for an art supply store. Let's use Stuart Semple as an example. Stuart Semple sells "Black 2.0 - The world's mattest, flattest, black art material" to everyone except Anish Kapoor. There's a bit of a backstory.

Here's what a feature file might look like, no_anish_kapoor.feature:

Feature: Stuart Semple will sell Black 2.0 to any artist except Anish Kapoor

    Scenario Outline: Anish Kapoor tries to buy Black 2.0
                       Given a purchaser, <purchaser name>, Black 2.0 is <saleable>
                       Examples:
                                 | purchaser name    | saleable     |
                                 | Anish Kapoor      | not saleable |
                                 | John Doe          | saleable     |
                                 | Jane Doe          | saleable     |
                                 | Manish Kapoor     | saleable     |
                                 | Anish Kapoorski   | saleable     |

With the feature file, it is time to create the step definitions, or 'glue' code. In this case, create src/test/java/com.gunnargissel.blacktwopointoh.stepdefs.NoAnishKapoor.java

The scenario outlined above only uses one keyword, "Given". That means we will create a single method in our step definition "glue" class that uses the @Given annotation. The objective of the @Given annotation is to write a regular expression that matches the scenario. In this case we want the <purchaser name> and the <saleable> qualities to be variables we pass to the stepdef. The rest of the @Given is business language that we pattern match on. The stepdef performs the role of a typical junit test, along with the setup for the test.

@Given("^a purchaser, (.*), Black 2\\.0 is (not saleable|saleable)$")
            public void testWhoCanPurchaseBlackTwoPointOh(String purchaser, String saleable) {
                    boolean actual_saleable = BusinessRules.saleable(purchaser);
                    boolean expected_saleable = saleable.equals("saleable");
                    assertEquals(expected_saleable, actual_saleable);
            }

Now that there is a scenario and a step definition, a junit runner class needs to be created like so:

@RunWith(Cucumber.class)
@CucumberOptions(
                       format={"pretty", "junit:target/cucumber/black_two_point_oh_sales_list.xml"},
                        features = {"classpath:no_anish_kapoor.feature"},
                        glue={"com.gunnargissel.cucumberexample.artfight.stepdefs"}
)
public class TestBusinessRules {}

After creating the necessary files, running mvn test will execute the feature scenario created above. Maven will create black_two_point_oh_sales_list.xml in the target/cucumber directory.

Further Resources

Just Enough Regular Expressions for Cucumber is a great guide that explains how to use regexes (and Java) with Cucumber. The same author also made a cheatsheet that is a helpful reference.

The artfight example code.

Thank you Ali Burçin Titizel for the header image

Tags: java cucumber junit testing qa qc

Get great articles about development every month here ⬇️

Meetings for Developers


As a developer advances in their career, they attend many, many meetings. "Too many meetings," most developers would say. Paul Graham talks about the "maker schedule" which is a model that rings true for me. A single meeting, especially a poorly run meeting, in the middle of a morning or afternoon, ruins half a day.

boring meeting

Thank you Ville Säävuori

Yet, meetings are valuable. A good meeting can help a team reach consensus, or pitch a new idea to management or create that elusive "synergy" between coworkers.

Part of the trouble for developers is that they are often not the meeting maker. Many people don't know how to run a good meeting.

Understanding Different Types of Meetings

Sometimes, meeting discontent comes from a lack of clarity about the purpose of the meeting. There are only a handful of constructive meeting types. Getting stuck in a meeting that doesn't match one of the following patterns is generally unpleasant and unproductive.

Meeting Types, Arranged by Purpose

  • Inform
  • This type of meeting is easy to set up and often easy to attend. These are just presentations to a group of people
  • Consult
  • Decision makers hold these meetings to gather input. What they do with the input is not part of the meeting, but these meetings offer the chance to speak up
  • Discuss
  • Discussion meetings bridge the gap between a consultation meeting and a collaboration meeting. There is back and forth, but a decision is not necessarily the product of the meeting. These aim to create something like a 'meeting of the minds'
  • Collaborate
  • This type of meeting is tough to run. The goal is to have the participants discuss and collectively decide something.

Be the Change You Want in the World

empty meeting room

Thank you Thoroughly Reviewed

Somewhere around mid-career, developers start hosting their own meetings. Make the world a better place by only hosting meetings that you would want to attend.

The key to hosting a good, productive meeting is to understand what the intent of the meeting is before meeting. The developer should decide what kind of meeting they are hosting before they host the meeting. Once the goal is realized, plan the meeting so that everything bends toward that goal.

Write an agenda - there is no such thing as a good meeting without an agenda. The act of writing the agenda, and assigning time estimates to each section of the meeting will make it clear if the meeting can be short or if the meeting will be too long.

Do not take notes at a meeting you host.

Do not take notes at a meeting you most.

Do not take notes at a meeting you host.

Get someone (not you) to take notes during the meeting. A designated note taker will leave you free to lead the meeting, present your material and guide discussion. Note takers can also provide feedback on your meeting performance afterwards. A record of what happened during the meeting is extremely useful to send to meeting participants.

Encouraging meeting makers to behave

If you are invited to a meeting without an agenda, ask the meeting organizer when they will send out the agenda. Be sure to ask far enough in advance for them to write the agenda.

If no one is taking notes for the meeting, offer to take notes. This will help ensure everyone ends up on the same page. You also have the option to frame the discussion.

If the meeting scheduled is lengthy, request to break it into smaller meetings, or that you get invited only to the part relevant to you. This doesn't always work - sometimes a person must develop an iron butt.

Conclusion

large meeting

Thank you Ricter Frank-Jurgen

Meetings are inescapable. As with all things that can't be changed, embrace them and make them your own to lessen your pain. Meetings don't have to suck - run your meetings so they don't suck, and gently encourage others to improve their meetings.

Remember, a meeting is a communication tool, just like email, Slack or JIRA. Use meetings well and lots of good can happen. Use them poorly, and suffer.

Tags: meetings personal-development business soft-skills leadership

Get great articles about development every month here ⬇️

Java 8 Functional Interface Cheatsheet


O'Reilly has a great in-depth article on the functional interface in Java 8. Here's a quick cheat sheet to keep track of the main types, what they do and common use cases.

Predicate

A Predicate returns true or false. These are used for filters or replacing big chains of if/else logic.

Function

A Function transforms data. These are used for maps, and other transformations. It is important to not mutate the original data that is passed in.

Supplier

A Supplier takes no arguments and returns a value of a known type. Fetching, reading or creating resources to be used by other functions are common use cases. Suppliers get things started.

Consumer

A Consumer accepts a single argument, but does not return any results. Consumers are where mutating functions, things with side-effects, should go. Consumers finish things.

Tags: cheatsheet how-to guide tutorial functional-programming java8

Get great articles about development every month here ⬇️

How to Write a Multiline Lambda in Java 8


For the most part, single line lambda functions are all you need.

This is a single line lambda:

Predicate<String> isBig = str -> str.length() > 10;

Sometimes one line is not enough to express the complexity of the lambda. How do you make a multiline lambda?

This is how:

//set up - elsewhere in the pseudo code:
public Sound getSound() throws MuteAnimalException {...}
Predicate<Sound> barks = sound -> Sound.valueOf("bark").equals(sound);


//payoff, a multiline lambda
Predicate<Animal> isDog = animal -> {
    try {
        return barks.test(animal.getSound());
    } catch (MuteAnimalException e){
        logger.severe(e.getMessage);
        return false;
    }
};
Tags: how-to tutorial guide functional-programming java8

Get great articles about development every month here ⬇️

Page 1 / 4 »