Map in a Java Stream, What is it?

Working with streams often requires 'mapping' of objects. What is mapping, and why do people do it with streams?

Mapping is how to convert one type of object to another with a stream. Say you have a set of Fruit and you want to show people what is in your set. It would be helpful to have a list of fruit names to do so.

fruitList.stream().map(fruit -> fruit.getName()).collect(Collectors.asList);

That's pretty simple; you can imagine how to do that in real life with a basket of fruit.

Pick up a piece of fruit, write its name down. Pick up another piece of fruit, write its name down, etc.

Mapping also lets you you can't easily simulate in real life. Say you have a Fruit set and you want Oranges, instead of Apples (I think this is closer to transmutation than swapping, but it's a metaphor, ymmv).

You can do that, with Java:

fruitList.stream().map(fruit -> {
    if( fruit instanceof Apple){
        return new Orange();
    }
    return  fruit;
}).collect(Collectors.asSet);

Have fun out there, and map all the things!

Filter Null Value from a List with Java8 Lambda


A common task with Java streams is to clean up the input data so later steps can work without thinking too hard. Likely the #1 most common cleanup step is to remove nulls from a Collection.

Streams make it easy:

myCollection.stream()
  .filter(Objects::nonNull)
  .do.what.you.need

Compare with the classic approaches:

while(myCollection.remove(null));
// do what you need, but you better not need that original list, because it's gone...
myCollection.removeAll(Collections.singleton(null));
// do what you need, but you better not need that original list, because it's gone...

Like the stream approach, these are short and sweet, but unlike the stream approach they modify the original list. The first example is also pretty slow.

I like the stream approach because I can chain additional tasks after the filter task, including map. sorted, reduce and more!. I find the traditional imperative iterative approach to be not only wordier, but conceptually harder to follow.

Make an Immutable Object - in Java


Immutable objects are objects that don't change. You make them, then you can't change them. Instead, if you want to change an immutable object, you must clone it and change the clone while you are creating it.

A Java immutable object must have all its fields be internal, private final fields. It must not implement any setters. It needs a constructor that takes a value for every single field.

Immutable objects come in handy in multi-threaded environments and in streams. It is great to rely on objects not changing mid-stream. Bugs caused by a thread changing another thread's object are often subtle and are very, very hard to track down. Immutable objects stop these whole class of problems in their tracks.

You don't have to take my word for it - see what experts around the web say.

Contents

  1. Making an Immutable Object
  2. Common gotchas
    1. Primitives
    2. Collections
    3. Arrays
    4. Objects
  3. How to Change an Immutable Object
    1. Where to put Builders?
  4. Handling Bad Data in Immutable Objects
  5. Wrapup

Gotchas!

a lonely tree on a rock

Lists, arrays, maps, sets and other non-immutable objects can be surprising. A private final object with no setter is fixed to the object it was initially assigned, but the values inside that object aren't fixed (unless the object is immutable).

That means you might have an ImmutableShoppingList myShoppingList = new ImmutableShoppingList(new String[] {"apples","anchovies","pasta"}) and expect that the shopping list will always have "apples", "anchovies" and "pasta".

Someone could call myShoppingList.getList()[0] = "candy bars"; and change your list to be "candy bars", "anchovies" and "pasta", which is unhealthy and clearly not what you want.

Primitives

Good news! Primitives are immutable, so you don't have to do anything special.

Collections

Good news! java.util.Collections provides a number of convenience methods that make converting a Collection to an UnmodifiableCollection a snap.

Check out:

Collections.unmodifiableCollection
Collections.unmodifiableList
Collections.unmodifiableMap
Collections.unmodifiableNavigableMap
Collections.unmodifiableNavigableSet
Collections.unmodifiableSet
Collections.unmodifiableSortedMap
Collections.unmodifiableSortedSet

I suggest you store the fields as generic Collections (List, rather than ArrayList), and make the unmodifiable in the constructor, like so:

public class ImmutableShoppingList {

    private final List<String> list;

    public ImmutableShoppingList(List<String> list){
        this.list = Collections.unmodifiableList(list);
    }

    public List<String> getList(){
        return list;
    }
}

This allows you to use IDE code generation to make the getters, which is nice, and contains all the input modifiers in one place, which is also nice.

Bad news! If you hang onto the reference to the collection when you create the collection, you can still modify it, even if you store it as an unmodifiable collection internally. Here's an example:

List<String> originalList = new ArrayList<>();
theList.add("apple");
ImmutableShoppingList blah = new ImmutableShoppingList(originalList);
originalList.add("candy bar");

The supposedly immutable shopping list started with an apple, and had a candy bar added to it after creation. What can we do about this?

Clone the list!

public class ImmutableShoppingList {

    private final List<String> list;

    public ImmutableShoppingList(List<String> list){
        List<String> tmpListOfHolding = new ArrayList<>();
        tmpListOfHolding.addAll(list);
        this.list = Collections.unmodifiableList(tmpListOfHolding);
    }

    public String[] getList(){
        return (String[]) list.toArray();
    }
}

When we create the immutable object, we deep clone the collection, which severs the connection to the original reference. Now when we run the "sneak a candy bar in" example, "candy bar" gets added to originalList, but not the ImmutableShoppingList.

Arrays

Bad news! Java doesn't have any convenient methods to prevent arrays from being modified. Your best bet is to either hide the original array and always return a clone, or to not use arrays in the underlying implementation and instead convert a Collection object to an array.

I prefer to stick with Collections, but if you must have an array in your object's api, this is the approach I would take:

public class ImmutableShoppingList {

    private final List<String> list;

    public ImmutableShoppingList(String[] list){
        this.list = Collections.unmodifiableList(Arrays.asList(list));
    }

    public String[] getList(){
        return (String[]) list.toArray();
    }
}

Objects

Object fields can be easy. If the sub-objects are also immutable, good news! You don't have to do anything special.

If the sub-objects are not immutable, they are a lot like a collection. You need to deep clone them, or the original reference can change your supposedly immutable data out from under your feet.

Often, you end up working with pre-existing mutable objects, either in your codebase, or in libraries. In this case, I like to create an immutable object wrapper class that extends the mutable class. I find a static getInstance(MutableObject obj) method can be helpful, but a constructor ImmutableObject(MutableObject obj) is also a useful thing to have.

What About When I Want To Change An Immutable Object?

a woman having a good time in a shop

It happens to everyone. You need an an object, but you don't know everything about the object. You can't quite commit to an immutable object.

In this case, I reach for the builder pattern.

The builder pattern creates a temporary object with the same fields as the desired object. It has getters and setters for all the fields. It also has a build() method that creates the desired object

Imagine a small immutable object:

class ImmutableDog {
    private final String name;
    private final int weight

    public ImmutableDog(String name, int weight){
        this.name = name;
        this.weight = weight;
    }

    public String getName(){
        return this.name;
    }

    public int getWeight(){
        return this.weight;
    }
}

Here's what the builder would look like:

class ImmutableDogBuilder {
    private String name;
    private int weight;

    public ImmutableDogBuilder(){}

    public ImmutableDog build(){
        return new ImmutableDog(name, weight);
    }

    public ImmutableDogBuilder setName(String name){
        this.name = name;
        return this;
    }

    public ImmutableDogBuilder setWeight(int weight){
        this.weight = weight;
        return this;
    }

    public String getName(){
        return this.name;
    }

    public int getWeight(){
        return this.weight;
    }
}

Note the setters I really like this pattern of returning this on each setters in builder classes, because it creates a very fluent api. You could use this ImmutableDogBuilder like this:

ImmutableDogBuilder dogBuilder = new ImmutableDogBuilder().setName("Rover").setWeight(25);

You can imagine in classes with more fields that this compacts your code a lot.

Where To Put The Builder?

There are two schools of thought here.

On the one hand, you can create a separate class for the builder. This is easy, it is very conventional, and your IDE will probably group the classes together, because they probably have similar names.

On the other hand, you can embed the builder class in the immutable object class as a public static inner class.

I prefer to embed builder classes in immutable objects, because I view the builder as a helper for the immutable object, and not a standalone thing. It keeps them together and tightly coupled.

What About Immutable Objects With Bad Data?

a very dirty car

It happens to everybody, especially if you accept input. Bad data!

Bad data is no good, but immutable bad data seems especially wrong - you can't even fix it!

I use two approaches to prevent bad data from getting turned into immutable objects.

My primary approach is a suite of business rules that test for sane, permissible data. The business rules look at builders, and if the builder passes, I deem it ok to create the immutable object.

My secondary approach is to embed a small amount of business logic in the immutable object. I don't allow required fields to be null, and any nullable field is an Optional.

For example:

class ImmutableDog {
    private final String name;
    private final Optional<int> weight

    public ImmutableDog(String name, Optional<int> weight){
        Objects.requireNonNull(name);
        this.name = name;
        this.weight = weight;
    }

    public String getName(){
        return this.name;
    }

    public Optional<int> getWeight(){
        return this.weight;
    }
}

This is an ImmutableDog that requires a name, but does not require a weight. It's important to know what to call a dog, but it's not strictly necessary to know that Fluffy weighs 15 lbs.

Objects.requireNonNull will immediately throw a NullPointerException if a name is not provided. This prevents the creation of a nonsensical immutable object. It also allows users of the immutable object (such as streams or functions) to skip handling nulls. There are no nulls here.

Using Optional<int> makes consumers of ImmutableDog immediately aware that they may have to handle a null. Providing the Optional api gives downstream users an easy, functional way of handling nulls.

Wrapup

a colorful celebration

Immutable objects require some special care and handling, but their utility is worth it.

The main principles to keep in mind are:

  1. Clone arrays, Collections and Objects internal to your immutable object
  2. Use builders when you need a mutable object
  3. Use Optional to indicate nullable fields in your object's api
  4. Fail fast on bad data - Objects.requireNonNull can help

Go forth, and stop mutating

Host a React App with Dropwizard


drop of water

Dropwizard is a great Java framework for building RESTful applications. It helps you quickly set up a server with a RESTful api, and has lots of useful features right out of the box for making production-grade apps.

React is a great Javascript library for building webapps. Via create-react-app, React comes with excellent tools for running a development server and creating a production build. The create-react-app production build makes a directory full of static html and Javascript files. You could host these with Apache, or nginx, or any variety of webservers.

It is convenient to minimize the number of pieces of infrastructure an application requires, so hosting the static files with the api server is appealing. The standard way to host static files with Dropwizard is to include them in the jar, so they are accessible from the classpath. There is no way to externally host static assets out of the box.

Fortunately, some kind souls created the Dropwizard Configurable Asset Bundle which allows you to configure an external directory to be hosted at a uri by Dropwizard.

Setup

origami wizard's duel

I've created a skeletal Dropwizard echo server and a teeny-tiny React app to go with it. The Dropwizard server is a plain vanilla configuration except for the addition of this dependency in the pom.xml

<dependency>
    <groupId>io.dropwizard-bundles</groupId>
    <artifactId>dropwizard-configurable-assets-bundle</artifactId>
    <version>1.2.2</version>
</dependency>

and this addition in the app.yml

assets:
  mappings:
    /assets: /
  overrides:
    /: /teeny-tiny-react-app/build

and this change to the configuration class:

... extends Configuration implements AssetsBundleConfiguration {
...
@Override
public AssetsConfiguration getAssetsConfiguration() { return assets; }
...

and this addition to the app initialization:

bootstrap.addBundle(new ConfiguredAssetsBundle("/assets/", "/", "index.html"));

and finally, this addition to the app run command:

env.jersey().setUrlPattern("/api/*")

But What Does It Mean?

These configuration changes add the dropwizard-configurable-assets-bundle to your project, assign the /assets classpath to the / uri, and the / uri to the /teeny-tiny-react-app/build directory.

They also include a setting that makes Dropwizard look for index.html if nothing matches the incoming uri.

Finally, they host the Dropwizard resources at /api/*.

How Do I Use It?

bulldozer

This configuration allows for two usage modes - development and production.

In development mode, proceed as normal. Fire up Dropwizard java -jar muh-sweet-rest-api.jar server app.yml then move over to your React dev environment and start coding while running npm run start.

In production mode, fire up Dropwizard, and then do a React build npm run build. This will create a production-optimized build of your React app, which you can place where Dropwizard expects to find it.

You'll have two options to view your React app - the default localhost:3000 or Dropwizard-served localhost:8085

The default port is hosted by React's built in server and has all the auto-reloading goodies you are used to.

Dropwizard's port hosts a static build, so it doesn't auto-reload as you code. It will, however, automatically serve new code if you manually deploy it.

Why should I do this?

girl on slide

You should do this if: * You are already running a Java backend * You want to deploy React changes separately from Java changes * You have an ops team that will let you touch the war deploy location, but not the html deploy location (or vice-versa) * Messing with proxying is difficult (environment specific)

You should not do this if: * You want the frontend and the backend in lockstep * Proxying is easy * You already have one or two html servers * You don't want to use Java

Credits

Taking Notes


I wrote earlier about why you might take notes. Here's how I take notes:

I've tried a lot of systems. I've tried Evernote. I've tried a wiki (several wikis, really). I've tried text files on the filesystem. I've tried Google Drive. None of these systems worked for me. Their organizational schemes, and the fact that I need a working computer to use them puts too many barriers between me, and the notes I want to write.

The note taking system that works best of me is Bullet Journaling, which is a structured way of categorizing and organizing notes. The practices that I find most useful are adopting a set of shorthand symbols for each note, keeping a weekly log, keeping a monthly calendar and keeping an index of my notes.

How I Take Notes

I take notes at all the meetings and trainings I attend. Sometimes my notes are just "I attended this meeting". Other times, I come out with a list of todos, and still other times I just come out with a bunch of things I want to remember.

Here's an example of notes I took during a project management training:

An example of what notes look like in a bullet journal

I kept track of what the training was about and observations on The Flight of the Phoenix, a training aid our instructor chose. I didn't have any todos or other particularly notable notes on this page, but it gives a flavor of how I use bullet journaling in the day to day.

My Preferred Set of Symbols

I adopted a set of symbols based on the bullet journal suggestions to indicate different types of notes. I went with a subset of the bullet journal symbols, because I found my todos are generally not very complicated. They either need to happen, happened or aren't going to happen. I didn't find moving them around from day to day very useful.

-

I use a dash to prefix general notes and observations

I use an open circle to indicate a todo. I put an "X" through the circle when it's done or cross it out when it becomes irrelevant.

👀

I use an eyeball for things I want to look into more

!

I use an exclamation point for things that I think are important

Keeping a Weekly Log

I work in an office environment, with semi-annual performance reviews. I find writing up what I did over the last six months a lot easier if I've kept track of it at some kind of high level overview. To that end, I use weekly logs

A detailed log of my weekly activities

I write out the log at the start of the week, then as I do things, I put a short note in on each day. "Attended stakeholder meeting," "Wrote workflow container," "Created permit entry UI," "Mentored junior developer," etc.

Keeping a Monthly Log

The weekly logs are great for looking back, but sometimes I want to look forward. I keep a couple months calendar in my journal to help plan when my busy periods are likely to be.

Here's what a fairly empty calendar on a new month looks like:

An empty calendar for a new month

As I learn new things, I put important events in my calendar, whether they are target release dates, testing cycles or demos. I also use these calendars to plan around personal events, like vacations or doctor appointments.

Keeping an Index

Keeping an index sounds boring and difficult, but I find that if I'm diligent, it's not particularly difficult, and it turns out to be one of the most useful parts of my journal.

An index of topics my work bullet journal covers

The three practices that make keeping an index easy are

  1. Number pages as you turn to them
  2. Title new entries before you write them
  3. Update the index as soon as you create a new entry

The payoff is six months after you've written a bunch and can't quite remember where that important technical decision originates from. Just look at the project meetings, or other promising entries and cut your search time down!

Ride off into the Sunset

Whatever method you choose to take notes in, having a system makes it easier to use the notes after writing them. I find the Bullet Journal technique very useful, but it took me a couple tries to settle in on how I wanted to record information. I'm still learning and changing how I take notes - never hurts to change!

Credits

Thank you Matt Reinbold for the notepad

Protect Yourself like the FBI — with Memos


Lots of paper

Donald Trump has been in the news a lot lately. Especially concerning FBI investigations and memos. It seems like everyone in Washington is fighting with memos - Comey, McCabe, Nunes - the list goes on and on.

Clearly memos are important, but they also seem distant and removed from programming. You programmers reading this article aren't in a fight with the president or the FBI1, so why should you care about memos?

Tools of Bureaucracy

Printing out memos

You should care about memos because we work in a fundamentally bureaucratic society run by managers2. It's likely that you work for an organization that is large enough to have its own bureaucracy - most programmers do.

If you aren't sure whether you work in a bureaucracy, here's a quick test:

  • Is there a process for getting hired?
  • Is here a process for leaving the company?
  • Is there a process for getting fired?
  • Is there a process for getting a promotion?
  • Does your boss have a boss?

If you answered yes to more than two of them, congratulations! You are a programmer and part-time bureaucrat.

What is a Memo?

Write things in a notebook

It's common for programmers to understand memos in Office Space terms. They are something supervisors do, and they really need TPS coversheets. While this is true3, it is a limiting conception of what a memo is.

Memo is short for "memorandum" which is from the Latin phrase memorandum est, "It must be remembered that..." A memo is some kind of documentation that jogs your memory. Of course, there are many different kinds memos in the modern world - policy memos, memorandum of understandings, meeting minutes, the list goes on.

From day-to-day programmer, the most important kind of memo is the memo-to-file. A memo-to-file is something you write after a conversation or an action that describes what happened. It isn't addressed to anyone, it's not trying to persuade anyone of anything. A memo-to-file simply records an event as you understood it at a certain point in time.

Put another way, a memo-to-file is a note with a date on it.

You should know that memos are being created about you. Your manager, coworkers, and stakeholders are all probably writing things down, about stuff you did!

Hold off on the panic attack. They probably aren't writing down bad things - the vast majority of memos are neutral. "Met with so-and-so, discussed project. Committed to reviewing Jira issues"

For the most part, these memos go unused. Written once, read never. They do, however, come in handy in a variety of situations, usually after something has gone wrong.

Concrete Examples

Filing cabinets full of memos

On the Road to a Job Hunt

Let's start right off the bat with the grim example. Your boss is not happy with your performance and thinks they may have to fire you. In bureaucratic organizations, there is a process connected with firing, and it typically starts with a performance improvement plan4.

Firing is expensive, and it is important to document a repeated pattern of behavior5. Because firing is expensive, many bureaucratic organizations recommend supervisors take actions to head off people getting even a performance improvement plan - mentoring meetings, feedback meetings, checkins, etc.

Supervisors document these informal actions with memos-to-file. "Counseled so-and-so that they need to inform stakeholders of a blown deadline before said deadline is blown." These notes build a case of a repeated pattern of behavior that supports the action they take down the road.

Reviewing What Went Wrong

A less grim occurrence is the post mortem, or after action report. A system failed, and the team wants to figure out what happened and why it failed. Keeping notes about who asked you to do what can be helpful when establishing a timeline. For example, "Chief stakeholder Jane wanted the big feature rolled out next week, so she asked me to do an unscheduled deploy on Friday."

They can also help establish mindset before a system failure - the best post mortems assume everyone is trying their professional best, so knowing why a person thought the action that caused the failure was a good idea is helpful, and memos-to-file can be a useful resource to determine the "why's".

Day-to-Day Disagreements

The most common (and least grim) place that memos-to-file can help a programmer out, is mild disagreement with stakeholders about feature requests and bug fixes. It's common to walk out of a meeting feeling like you have a shared understanding about what to do next, only to find a week or a month later that you did not.

A contemporaneous memo of what you understood as you left the meeting is useful, because it can help you figure out where the disagreement started. They can be useful in figuring out who was 'wrong' but this is of limited usefulness when everyone is on the same team working towards shared goals in a supportive fashion.

In the event that you are working on a contentious team, this type of memo can be useful protection - "See, I did what they said! It's not my fault they changed their mind." Hopefully you are not on a team like this, but even the best teams sometimes veer unexpectedly towards blame and contention.

End of the Year Writeups

Everyone with a boss must, from time to time, say what they did and why it was good for the company. This often comes in the form of an annual performance review with a short write up about what you did and a quick meeting with your boss. These meetings have a high impact on your promotion and bonus potential. It is in your best interest to have a good summary of your accomplishments at hand.

A good collection of memos documenting the people you talked to and the actions you took, as well as the motivations and results, makes quick work of writing end of the year accomplishments.

Be Prepared

A hall of filing cabinets

Write memos, not with the intention of needing them, but with the knowledge that you might need them as circumstances change. "Be Prepared" is an excellent motto, whether you are a scout, a backup-conscientious admin or a programmer-bureaucrat. What is a happy situation today, may turn tomorrow. Memos-to-file can help bail you out when things turn sour.

Happy writing!

Credits

Thank you Kiran Foster for the picture of a filing cabinet Thank you mcfarlandmo for the picture of many filing cabinets Thank you Florian Hufsky for the picture of a notebook Thank you Orin Zebest for the picture of a paper cliff Thank you No More Plains for the picture of a printer

Footnotes

1: Unless you are in a fight with the president and/or the FBI - in that case, I'm interested and want to hear more; dish! 2: Say what you will about James Burnham and The Managerial Revolution, I think there is something to the description of society there 3: Not really. 4: Your organization may call these something else, but the idea is a last chance plan before they show you the door 5: For liability and unemployment insurance reasons both

Turn Your Spaghetti Code into Functions - Part 3


Start with Part 1 and Part 2 first.

Picking up where we left off in Part II, let's set the stage for using a validator. We left our example elephant in a cool convertible, but let's upgrade his ride to a rocket ship. We'll start like all good rocket scientists - with a round of cleaning!

elephant driving a space shuttle

After mise en place, we will be ready to implement a Validator and Result, which will create a single, standard, expandable format for business rules and an api for consuming results from business rule queries. No more guessing what a rule-compliant call looks like! No more stringing together crystal towers of nested boolean logic!

Cleanup

There are a couple places where we used .and() to combine predicates in an if block. Replace those with an independent predicate, so all if blocks refer to only one predicate

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> isDirigibleForbiddenArea = 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);

    static final Predicate<WidgetTransfer> parterTransferReqs = trans -> isPartner.and(isPartneringArea.negate()).test(trans);
    static final Predicate<WidgetTransfer> dirigibleTransferReqs = trans -> isPartner.and(isDirigibleForbiddenArea.negate()).and(isDirigibleCategory).test(trans);
    static final Predicate<WidgetTransfer> friendsAndFamilyReqs = trans -> isFriendsAndFamily.and(isFriendAndFamilyDiscountLegal.negate()).test(trans);
    static final Predicate<WidgetTransfer> internalBlockReqs = trans -> isInternal.and(isBlockSize).test(trans);
    static final Predicate<WidgetTransfer> internalTotalCapReqs = trans -> isInternal.and(isTotalOverCap).test(trans);

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

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

        if (parterTransferReqs.test(transfer)) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        }

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

        if (friendsAndFamilyReqs.test(transfer)) {
            businessRuleErrors += "This area is not a transfer eligible area. ; ";
        }

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

        if (internalTotalCapReqs.test(transfer)) {
            businessRuleErrors += "This transfer is too large. ; ";
        }

        return businessRuleErrors;
    }

The above code has a lot of boiler plate - but it's boiler plate that is invisible to the average Java dev. All those if blocks are boiler plate. You have to type them again, and again. What if I told you, you could use a validator and clean up all your business rules? Think of the miles of conditionals in your business code, and imagine each reduced to a single composable function call.

Boilerplate Removal

Another problem is that checkWidgetTransfer returns a string. This pushes the responsibility for determining if an error has occured onto the calling method. All checkWidgetTransfer callers need a section that looks like this:

String result = checkWidgetTransfer(transfer);
if(null == result || 0 == result.size()) {
    //continue
}else{
    handleError(result);
}

Multiply this by every bizarre process that Bob in accounting, Carol in sales uses and Duane in HQ uses. It can get.... big.

We can save on typing, share conditionals and share business logic by using the Validator and Result technique. Here's what it looks like from the caller's perspective:

checkWidgetTransfer(transfer).onError(err -> handleError(err));
//continue

This provides an api for the caller that indicates what an error condition is. Callers no longer have to guess that an empty string is a pass, and a non-empty string is a fail. The api provides a place to put error handling, which can take an existing function, or have an on-the-fly lambda in place. Sweet!

You still have to write up Bob, Carol and Duane's favorite workflow. but things are compact and you don't have to go down many twisty branches, each more a like than the last.

Now with a Validator

What if you didn't have to write any if/then/else statements? What if you you only had to write the logic, and something else would handle stringing that logic together. A validator can make that possible.

Here's what the implementation of checkWidgetTransfer looks like, using a validator:

public static final Result<WidgetTransfer> checkWidgetTransfer(WidgetTransfer transfer) {
    Validator<WidgetTransfer> widgetTransferValidator = new Validator();
    widgetTransferValidator.addRule(suffientAmount, "Insufficient balance to transfer");
    widgetTransferValidator.addRule(parterTransferReqs, "This area is not a transfer eligible area");
    widgetTransferValidator.addRule(dirigibleTransferReqs, "D Category Transferer can only be transferred in transfer area 213");
    widgetTransferValidator.addRule(friendsAndFamilyReqs, "This area is not an eligible area");
    widgetTransferValidator.addRule(internalBlockReqs, "Amount is too small for I type transfer");
    widgetTransferValidator.addRule(internalTotalCapReqs, "This transfer is too large");
    return widgetTransferValidator.validate(transfer); 
}

The validator can take as many rules as needed, and each rule gets a name and a matching message. The validator ensures every rule is applied to the transfer, and a Result is returned, containing either the error messages or the transfer.

Implementation Details

I implemented a Validator as a HashMap of functions to error strings. The validate method tests each function, and if the test is true, collects the matching message in the Result.

I implemented Result as an Either. Callers have the option of getting a boolean hasErrors or passing in a Consumer to onError. The important thing about a Result is that it is never null and it guides the developer to the correct method of handling an error.

Wrap up

With just a tiny push, we helped our elephant reach orbit. We've gone from bog standard spaghetti business logic to pure functions embodying composable business logic. Thank you for coming along for ride. I welcome comments or feedback at gunnar@gunnargissel.com, or @monknomo on Twitter

Credits

Thank you Oliver Dodd for the elephant

Thank you NASA, ESA, N. Smith (U. California, Berkeley) et al., and The Hubble Heritage Team (STScI/AURA) for the Carina Nebula

Photo of the shuttle Endeavour by Mr. Littlehand

Boiler Plate from Les Chatfield

Blastoff by Matthew Lancaster

Java 8 Stream Cheatsheet


Streams are a way of working with objects in a collection.

Streams allow a person to say what they want to have at the end. Contrast that with the imperative approach that requires a person to say how to collect what they want to have at the end.

Streams also allow for trivial multi-threading.

Basic Stream Methods

There are a couple basic methods that govern stream interaction. The general idea behind these methods is manipulating a collection of objects so that the only objects remaining are the ones you want.

If I had to pick the indispensable methods for working with a Stream, I'd say Map, Filter, and Collect are on it.

Filter Filter takes a Predicate, and leaves only the values that the Predicate is true for.

Map Map takes a Function, and transforms the values in the stream from one type to another

Sorted Sorted takes a Comparator, and orders the values in the stream according to the ordering specified in the Comparator.

Collect Collect stops the stream and puts the values from the stream into some kind of Collection, like a List.

toArray toArray() stops the stream and returns an array with the elements contained in the stream.

Finders

A common task, when using a collection, is to get an element out of the stream. These are generally used after Filter and Map, to guarantee that the element you find matches some criteria and is of the preferred type.

findAny() findAny() returns an Optional containing an element in the stream (or nothing, if the stream is empty). Order is not respected here, especially in parallel stream situations.

findFirst() findFirst() returns an Optional containing the first element in the stream (or nothing, if the stream is empty). Order is respected when using this method

max(Comparator comparator) max returns the maximum element of the stream for the given Comparator

min(Comparator comparator) min returns the minimum element of the stream for the given Comparator

Matchers

Another common task is to determine if the objects meet some given criteria. These "matcher" methods indicate whether some, none or all the objects in a stream meet a given criteria.

allMatch(Predicate pred) Returns true if all elements in the stream match the Predicate

anyMatch(Predicate pred) Returns true if any elements in the stream match the Predicate

noneMatch(Predicate pred) Returns true if no elements in the stream match the Predicate

Stream Modifiers

From time to time, a stream is not quite the "right" stream. These methods create a new stream with slightly different characteristics than the old stream, whether it is a different length, different starting point, or a guarantee that only unique objects are contained.

distinct() distinct() creates a new stream that has only distinct elements (based on .equals) in it

limit(int maxSize) limit(maxSize) creates a new stream by truncating the original stream to be no longer than maxSize.

skip(long n) Skips the first n elements of the stream, and creates a new stream out of the rest

sorted() Creates a new stream, where the elements are sorted according to natural order

sorted(Comparator comparator) Creates a new stream, where the elements are sorted according to the Comparator

Stream Characterizers

Another common task, when working with collections, is to determine some characteristics about the collection as a whole. The stream api provides a method to figure out how big a given stream is.

count() count() counts the number of elements in the stream

Newb Valley


There comes a point when you are good at what you do. If you're like me, you've been pretty good at things for a long time - pretty good at school, pretty good at work, pretty good at structured environments that provide a path and use skills you already know. This is a very comfortable place to inhabit. It comes with respect, both self respect, and respect from your peers and colleagues.

As long as you stay in your structured environment, on the path that uses/improves your existing skills, getting better feels easy - or at least, predictable. You put in effort, your skills improve in a (more or less) linear fashion.

Sooner or later, you may have the desire to learn something outside of your existing expert skillset. If you're like me, you are surprised when this hurts. Suddenly, you are not good at what you are trying to do. The shock of a drop in your skill level is disconcerting, and the temptation to retreat back to the comfort of your old groove is strong.

To pick a concrete example, you may be a talented developer. You can pick up new languages, or build new systems. If there is something programming-related you don't know, you have a good idea how long it will take to learn it, and how you should go about learning it.

But one day you become a lead developer, or transition into the management. Suddenly, you need skills that you haven't practiced for years and don't have a clear path from where you are.

Welcome to Newb Valley!

It's the difference between your hoped for path and your actual path that causes a lot of pain. There are a couple things you can do to reduce your pain and power through Newb Valley

  1. Know it is coming
  2. Have a plan to bridge the gap
  3. Forgive yourself for mistakes

Know it is Coming

This is an easy, but critical step. If your new found lack of competence surprises you, the impulse to pack it in and retreat to comfort may prove overwhelming. Instead, lean in to your newb-ness. Embrace your lack of competence - after all, if you knew what to do, you wouldn't be learning.

Developing a level of comfort with uncomfortable situations will go far.

Have a Plan

One way of developing and keeping comfort in the face of uncomfortable situations is to have a plan to lean on. You know you're in for an uphill struggle, so figure out in advance how you want to climb that hill. Give yourself some smaller goals and ways to check on your progress.

Forgive Yourself

You will be less competent for some amount of time.

That means you'll make mistakes. You'll blow deadlines. You'll deliver less scope than you had planned. Your plan to progress to the next level will be wrong.

Take a breath, re-evaluate, learn a lesson and move on.

You'll climb out of Newb Valley before you know it

Credits

Thank you Sandy Brown Jensen for the picture of the mountain climbers

Turn Your Spaghetti Code into Functions - Part 2


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

Page 1 / 5 »