Pure Bliss With Pure Functions in Java

<strong>See how pure functions apply to Java and how the divide between OOP and FP doesn't have to stop teamwork and collaboration when working ...</strong>

See how pure functions apply to Java and how the divide between OOP and FP doesn't have to stop teamwork and collaboration when working ...

Writing software is hard.

Writing good software is harder.

Writing good, simple software is the hardest.

Writing good, simple software as a team is the hardest… est.

You shouldn’t need several hours to understand what a method, class, or package does. If you need a lot of mental effort to even start programming, you will run out of energy before you can produce quality. Reduce the cognitive load of your code and you will reduce its amount of bugs.

“This is puristic nonsense!” you say. “Intellectual masturbation! Software should be modeled after real-world, mutable objects!”

I’m not saying you should go on a vision quest and return as a hardcore functional programmer. That wouldn’t be very productive. Functional and object-oriented programming can complement each other greatly.

I will show you how.

But first, let’s get to the what and why of it.


What?

A pure function is a function that:

  1. …always returns the same result, given the same input.
  2. …does not have any side effects.

Put simply: When calling a pure function with input A, it always returns B, no matter how often, when and where you call it. Also, it does nothing else.

In Java, a pure function might look like this:

public static int sum(int a, int b) {
    return a + b;
}

If a is 2 and b is 3, the result is always5, no matter how often or how fast you call it, even if done so concurrently.

As a counterexample, this would be an impure function:

public static int sum(int a, int b) {
return new Random().nextInt() + a + b;
}

Its output could be anything, no matter the input. It violates the first rule. If this would be part of a program, the program would become nigh impossible to reason about.

An example of the second rule, a function that has side effects:

public static int sum(int a, int b) {
writeSomethingToFile();
return a + b;
}

Even though this method is static and always returns the same value, it is impure. It does more than it advertises. Even worse, it does so secretly.

These examples may look simple and harmless. But impurity adds up quickly.


Why?

Let’s say you have a decent-sized program you want to add a new feature to. Before you can do so, you’ll have to know what it does. You will probably start by looking at the code, reading documentation, following method calls, et cetera.

While you are doing this, you are creating a mental model of the program. You are trying to fit every possible flow of the data inside your mind. A human brain is not optimised for this task. After a few state changes here, some conditionals there, you start to get somewhat foggy.

Now we have a breeding ground for bugs.

So how can we prevent this? By reducing the complexity of our program, for starters. Pure functions can be of great help here.


How?

A great way to clarify complex code is breaking it up into smaller, more manageable pieces. Ideally, every bit has its own responsibility. This makes them easier to reason about, especially if we can test them in isolation.

While untangling the spaghetti, you will notice you are now able to free up some of your intellectual capabilities. You are slowly getting at the core of the problem. Every piece that can stand on its own can be moved and you are left with the essence of your program.

Finding that bug is much easier now. So is adding a cool new feature. To make things more concrete, here are some guidelines.


Partition

Firstly, partition the bigger pieces by their functionality. Think about the various parts of the solution and how they interconnect.


Break Up

Break up those parts into composable units. Define a function that can be used to filter items from a list, or an action that can be re-used for every single item. Maybe add helper functions that encapsulate logic that would otherwise end up inside deeply nested code.


Document

Write documentation when you feel you are ‘done’ implementing a unit. This will help you see the logic from a different perspective and reveals unforeseen edge cases. If necessary, add unit tests to further define the program’s intents.

Rinse and repeat.

The definition of done is personal and can change over time. Don’t overdo it. If it’s still not clear enough, you will find out later. If not during a code review, maybe when adding a new feature to the project.

So far, so good, right? Theory always sounds great. But we need some convincing in the form of a practical example.


An Example

Let’s say we have a client that sells IoT devices. Customers can install these devices in their home and connect them to the internet. They can control these devices with an app on their mobile phone.

Our client wants their customers to receive a push notification if their device has been offline for a while, so they have a chance to reconnect the device. If it stays offline, they want to push notifications periodically, to remind the customer to reconnect the device. But not too often, since they don’t want unstable devices to cause an endless stream of warnings.

Also, our customer wants to be able to adjust the thresholds for sending these messages. At runtime, without having to pay us to do it for them. There should be at least one threshold, but there could be any number of them.

Using our backend software, we can detect the online status of these IoT devices. If they go offline, we store the moment they went offline.


Naive Implementation

How can we implement this? This feature looks quite simple on paper, so we could just start and see how far we’d get.

After doing some scaffolding we quickly get to the core of the problem: determining whether notifications should be sent for each offline device. Sounds simple, right?

public void run() {
// @todo Send notifications for offline devices
}

We add some for loops here.

for (Map.Entry<Device, Instant> offlineDevice : offlineDevices.entrySet()) {
for (Duration threshold : thresholds) {
// ...
}
}

Some if statements there.

if (firstThresholdWasPassed) {
// ...
}

Looking good!

Wait, there’s a special case for the last threshold.

if (i == thresholds.size()) {
// ...
}

Oh, and one for a single threshold.

if (thresholds.size() == 1) {
// ...
}

Crap, we forgot to check if the notification for each threshold was already sent. In 3 places.

if (!lastOfflineNotificationInstant.isPresent()) {
// ...
}

And what if it was sent before the device went offline?

if (Duration.between(disconnectInstant, lastOfflineNotificationInstant.get()).isNegative()) {
// ...
}

Oh my.

SonarLint is foaming at the mouth, telling us to reduce the cognitive complexity of 69, where 15 is allowed. The unit tests are getting quite complex as well, having to use an external Clock an whatnot.

We cannot let our future selves see this, they’d kill us!

Luckily, we have our three-step program. Let’s begin at 1.


Partitioning by Functionality

Now, to untangle this mess. Our first step is to partition the bigger pieces by functionality, to think about the various parts of the solution and how they interconnect.

We could visualize our problem by drawing a timeline, starting at the instant a device went offline:

On the timeline we can plot the various thresholds:

Every time our job is running, we have arrived at a certain point on the timeline. If we have not yet passed a threshold, nothing needs to be done:

If we do pass a threshold, we should send a notification and remember the time we sent it:

Next time we run our job, we should take the sent notification into account. If we have passed a threshold that the user was already notified about, we do nothing:

Only when passing another threshold, we can send another notification:

And so on and so forth.

How do we express this in code? It seems we need to know:

  • Which threshold (if any) we passed last.
  • Which threshold (if any) we sent the last notification for.

That’s it, basically. If we know these, we can determine if we should send a notification. All the special cases and complexity are captured by these two core parts of our solution.

Now we need to see if we can simplify our solution any further by breaking it up into composable parts.


Breaking It Up Into Composable Units

The two above statements look awfully familiar, no? That’s because they are. They both define a threshold or nothing. They both need similar input:

  • The moment the device went offline.
  • The threshold(s).
  • The point in time we are currently evaluating.

Now that we know the input and output of our unit, we can define its signature. Using the fancy java.time API, we can express it as such:

Optional calculateLastPassedThreshold(Instant start, Instant current, Duration[] thresholds);

We can now use this function to get both thresholds we needed. Did I say function? Let’s make that a pure function: declare it static and make sure it’s deterministic and doesn’t create any side effects:

static Optional<Duration> calculateLastPassedThreshold(Instant start, Instant current, List<Duration> thresholds) {
Duration timePassed = Duration.between(start, current);

if (timePassed.compareTo(thresholds.get(0)) &lt;= 0) {
    return Optional.empty();
}

for (int i = 0; i &lt; thresholds.size(); i++) {
    if (timePassed.compareTo(thresholds.get(i)) &lt;= 0) {
        return Optional.of(thresholds.get(i - 1));
    }
}

return Optional.of(thresholds.get(thresholds.size() - 1));

}

There! Aside from its breathtaking purity, it has another pro: It can be unit tested very easily. You don’t need mocks nor external clocks to test this part of the code.

Can you feel the amount of space your brain just regained? When programming the rest of our solution, we can just trust that this function will do what it says.

So what’s left?

We can now determine, for each device, if we should send a notification. Again, we can isolate this part of our program, make it pure and test it thoroughly:

static boolean shouldSendNotification(Instant jobStart, Instant deviceOffline, Instant lastNotification, List<Duration> thresholds) {
Optional<Duration> lastPassedThreshold = calculateLastPassedThreshold(deviceOffline, jobStart, thresholds);

if (!lastPassedThreshold.isPresent()) {
    return false;
}

if (lastNotification.isBefore(deviceOffline)) {
    return true;
}

Optional&lt;Duration&gt; lastPassedThresholdNotifiedAbout = calculateLastPassedThreshold(deviceOffline, lastNotification, thresholds);

return !lastPassedThreshold.equals(lastPassedThresholdNotifiedAbout);

}

Or, more concisely:

static boolean shouldSendNotification(Instant jobStart, Instant deviceOffline, Instant lastNotification, List<Duration> thresholds) {
Optional<Duration> lastPassedThreshold = calculateLastPassedThreshold(deviceOffline, jobStart, thresholds);

return lastPassedThreshold.isPresent() &amp;&amp; (lastNotification.isBefore(deviceOffline) || !lastPassedThreshold.equals(calculateLastPassedThreshold(deviceOffline, lastNotification, thresholds)));

}

And when you know the device has no previously sent notifications, you can just use:

static boolean shouldSendNotification(Instant jobStart, Instant deviceOffline, List<Duration> thresholds) {
return calculateLastPassedThreshold(deviceOffline, jobStart, thresholds).isPresent();
}

Now the only thing left is calling these functions for every offline device, every time our job runs:

public void run() {
Instant jobStart = Instant.now();

offlineDevices.entrySet().stream()
        .filter(offlineDevice -&gt; pushNotificationService
                .getLastOfflineNotificationInstant(offlineDevice.getKey())
                .map(instant -&gt; shouldSendNotification(jobStart, offlineDevice.getValue(), instant, thresholds))
                .orElseGet(() -&gt; shouldSendNotification(jobStart, offlineDevice.getValue(), thresholds))
        )
        .forEach(offlineDevice -&gt; pushNotificationService.sendOfflineNotification(offlineDevice.getKey()));

}

We’re bordering on iamverysmart-territory here. Not everyone likes this style of coding and it is arguably harder to parse than a more traditional style. So the least we can do is document our units.


Documenting Our Solution

Aside from playing nice with others, describing our code can also help us catch that last bug, or reveal an edge case we hadn’t thought of yet.

Documentation in our case most obviously comes in the form of Javadoc, for example:

/**

  • Checks whether a notification should be sent by determining which threshold has been passed last for the
  • calculated amount of time passed between the device going offline and the job running.
  • @param jobStart The instant the job calling this function was started.
  • @param deviceOffline The instant the device went offline.
  • @param lastNotification The instant the last notification was sent.
  • @param thresholds The list of notification thresholds.
  • @return True if the notification should be sent, false if not.
    */
    static boolean shouldSendNotification(Instant jobStart, Instant deviceOffline, Instant lastNotification, List<Duration> thresholds) {
    // ...
    }

Unit tests can also be regarded as documentation since they can define the intended use of the program under test very precisely. In the case of pure functions, where you often need to test a list of varying input, parameterized tests can come in handy.

While writing tests and documentation, we shift our thinking from the problem to the solution, and its users. By thinking from this other perspective, subtle problems in our code are highlighted and can be fixed on the spot.


Afterthoughts

It can be hard to convince others or even yourself of the value of this approach. Churning out functionality has more obvious business value than meticulously crafting software.

When approaching existing code this way, you can end up wasting hours while adding no measurable (short-term) value. Try explaining that during the daily stand-up.

On the other hand, preventing a bug is cheaper than fixing it, and the more clean your code is, the less time it takes to add a feature or find an error, especially if the code hasn’t been touched in a while.

Somewhere along the line, the balance is tipped. On one side lies the holy grail, the unachievable perfect piece of software. On the other side, the unmaintainable spaghetti monster slithers, shitting on deadlines, ballooning budgets and destroying teams.

Listen to your instincts, communicate your intentions, listen to your peers and learn from one another: making great software is an ongoing (team) effort.


Source code

All code referenced in this post is open source and can be found in its GitHub repository. Feel free to check it out, tinker with it and come up with better solutions.

Java Fundamentals: Learn Java for absolute beginners |Simpliv

Java Fundamentals: Learn Java for absolute beginners |Simpliv

Java Fundamentals: Learn Java for absolute beginners

Description
This is the best course to learn to program in Java in Spanish from scratch and without any experience in this fabulous programming language. This is the first course where we will study the Fundamentals of Java, and we will take you step by step until you acquire the bases of the Java language and you can start to study more advanced Java topics.

The content is divided into perfectly structured levels, each level supported by the previous one, with the aim of adding Java knowledge incrementally and so you can focus on mastering the issues little by little and gradually. So ensure the success of your Java training.

We will also offer support for any doubts about the didactic material included in this Java Fundamentals course.

We manage a new teaching methodology that we have called Speed ​​Learning. This methodology consists of concise videos that go directly to the point to be studied, complemented by eBooks with explanations and step-by-step images (which you can print, or search for any text you need, or use for your offline study), since As we know we can not do text search within a video. In addition, our methodology includes perfectly structured and very didactic exercises that will allow you to accelerate your eLearning learning. No loss of time in videos where you have to watch the instructor codify an exercise, too much theory, little practice or anything like that. Our Speed ​​Learning methodology guarantees that in the shortest possible time you will acquire the necessary knowledge for the Java professional and working world.

The Java Fundamentals course includes the following topics for study:

Lesson 1 - Starting with Java Technology

The amazing world of Java programming

What is Java technology (from a practical approach)

Our first Java program from scratch

Lesson 2 - Variables and Operators in Java

Use of Variables in Java and what we use them for

Types of Data in Java and how they are classified

Management and Classification of operators in Java

Lesson 3 - Control statements in Java

Using the if-else structure and where to use it

Handling the switch structure and when applying it

Lesson 4 - Handling Loops in Java

Use of the for loop and its use

Using the while loop and how to apply it

Use of the do-while loop and when to use it

Lesson 5 - Object Oriented Programming

Introduction to Object Oriented Programming (OOP)

Handling Classes in Java

Use of Objects in Java

Lesson 6 - Functions in Java

Declaration of Methods or Functions in Java

Use and call of functions in Java

Lesson 7 - Data Management in Java

Using Arrays in Java

Management of Matrices in Java

Lesson 8 - Inheritance in Java

Inheritance Management in Java

Use of superclasses and subclasses in Java

Final Level Laboratory

Final Exercise where everything learned in this Level is integrated

At the end you get a certificate of having completed the Java Fundamentals course.

We wait for you on the other side.

Ing. Ubaldo Acosta

Founder of Global Mentoring

Passion for Java Technology

Who this course is for:

Anyone who wants to learn how to program in Java
Basic knowledge
Basic knowledge of PC use
Basic management of an operating system such as Windows, Mac or Linux
It is not necessary to know how to program, we will start from scratch !!!
The attitude and desire to start coding and learning Java once and for all from scratch!
What will you learn
Have the basics of the programming language with Java
You will know the basic syntax of the Java language
Manage the concept of Variables and Operators in Java
We will study Object Oriented Programming with Java
You will learn the Control Statements and Loops in Java
We will see the concept of Functions with Java
We will study the concept of Inheritance in Java
We will learn to use Arrays in java
We will handle the concept of Matrices in Java
We will learn to Design Classes in Java
We will make a final application with everything learned in the course
To know more:

Java Essentials : Learn Core Java From Basic to Advance

Java Essentials : Learn Core Java From Basic to Advance

Learn Java Programming Using Practical Assignments. Start Building Back-end Web Applications Robust Test Automation Frameworks By End Of The Course. Learn More!

Description
This is only Java related course and it's great because it covers just the right amount of Java which is needed to leaning programming, java.

This is a comprehensive yet simple course on java programming language and it concentrates on Java programming concepts.

*************************** No Prior Coding Experience Needed ***************************

This course assumes that you have no programming background. If you have some experience then, it's just a bonus point. You have never code, have some experience or have a lot of experience any other programming language, this course is one stop place for you.

Java is one of the most and useful programming languages to learn You can build back-end of web applications and build robust test automation framework. Specially for Selenium WebDriver GUI automation, Java is most popular choice and has the largest community.

Each lecture consist of a video screencast and code files

There are quizzes, homework to test your knowledge

High focus on practice and asking questions

You will also learn coding best practices

Market is never short of jobs in Java programming language, there are ample of jobs in both Java development and Automation Testing using Java.

What are you waiting for? Enroll today and learn the powerful Java language !!!

Basic knowledge
Nothing else! It’s just you, your computer and your hunger to get started today
Java concepts are covered in the course, no experience needed
Windows/MAC computer
What will you learn
You will be able to EXPLAIN, DESIGN and IMPLEMENT efficient java Programs
You will be confident to clear test automation interviews
Understand the concepts of Object Oriented Programming Language
Complete understanding of java
Expert-level knowledge of Java code (+ advanced tips and tricks used by the pros)
Suitable for beginner programmers and ideal for users who learn faster when shown
To learn more:

Fundamentos de Java: Aprende Java desde cero, sin misterios | Simpliv

Fundamentos de Java: Aprende Java desde cero, sin misterios | Simpliv

Fundamentos de Java: Aprende Java desde cero, sin misterios

Description
This is the best course to learn to program in Java in Spanish from scratch and without any experience in this fabulous programming language . This is the first course where we will study the Java Fundamentals, and we will take you step by step until you acquire the basics of the Java language and so you can start studying more advanced Java topics.

The content is divided into perfectly structured levels , each level supported by the previous one, with the aim of adding Java knowledge incrementally so that you can focus on mastering the issues little by little and gradually. So ensure the success of your Java training.

In other offer support of any doubt teaching materials included in this course Fundamentals of Java.

To make matters worse, we handle a new teaching methodology that we have called Speed ​​Learning. This methodology consists of concise videos that go directly to the point to study, complemented with eBooks with explanations and step-by-step images (which you can print, or search for any text you need, or use for your offline study), since as we know we cannot do text search within a video. In addition, our methodology includes perfectly structured and very didactic exercises, which will allow you to accelerate your eLearning learning. Without wasting time on videos where you have to watch the instructor codify an exercise, too much theory, little practice or anything like that. Our Speed ​​Learning methodology guarantees that in the shortest possible time you will acquire the necessary knowledge for the professional and professional world of Java.

The Java Fundamentals course includes the following topics of study:

Level. Java basics

Lesson 1 - Starting with Java Technology

The amazing world of Java programming
What is Java technology (from a practical approach)
Our first Java program from scratch
Lesson 2 - Variables and Operators in Java

Use of Variables in Java and what we use them for
Data types in Java and how they are classified
Operator Management and Classification in Java
Lesson 3 - Control sentences in Java

Use of the if-else structure and where to use it
Management of the switch structure and when to apply it
Lesson 4 - Cycle Management in Java

Use of the for cycle and its use
Use of the while cycle and how to apply it
Use of the do-while cycle and when to use it
Lesson 5 - Object Oriented Programming

Introduction to Object Oriented Programming (OOP)
Class Management in Java
Using Objects in Java
Lesson 6 - Functions in Java

Declaration of Methods or Functions in Java
Use and call of functions in Java
Lesson 7 - Data Management in Java

Using Arrangements in Java
Matrix Management in Java
Lesson 8 - Inheritance in Java

Inheritance Management in Java
Use of superclasses and subclasses in Java
Final Level Laboratory

Final Exercise where everything learned in this Level is integrated
At the end you get a certificate of having completed the Java Fundamentals course.

We wait for you from the other side.

Ing. Ubaldo Acosta

Founder of Global Mentoring

Passion for Java Technology

Who this course is for:

Anyone who wants to learn to program in Java
Basic knowledge
Basic knowledge of PC use
Basic operation of an operating system such as Windows, Mac or Linux
It is not required to know how to program, we will start from scratch !!!
The attitude and desire to start coding and learning Java once and for all from scratch !!!
What will you learn
Have the basics of the programming language with Java
You will know the basic syntax of the Java language
Will handle the concept of Variables and Operators in Java
We will study Object Oriented Programming with Java
You will learn Control Sentences and Cycles in Java
We will see the concept of Functions with Java
We will study the concept of Inheritance in Java
We will learn to use Arrangements in java
We will handle the concept of Matrices in Java
We will learn to Design Classes in Java
We will make a final application with everything learned in the course
To continue: