Friday, 22 August 2014

Java 8 : Functional VS Traditional

The business logic is the same :

Given a String expression composed of visits / time like : "1/24h,1..3/3h,5/*"
Then the result should be the following list of Strings 
"1/24h",
"1/3h","2/3h","3/3h",
"5/1h","5/2h","5/3h","5/4h","5/5h",until ,"24/1h"


So, 2 things need to be solved, the dots and the stars for the visits and for the time.

I will use Java 8 , but  I'll show you the difference between implementing this logic using Functional  and implementing it in a traditional way with loops and ifs.

package com.marco;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
public class ExpressionConverter {
        private static final String COMMA = ",";
        private static final String SEPARATOR = "/";

        private final ImmutableList<Function<String, List<String>>> visitFunctions;
        private final ImmutableList<Function<String, List<String>>> timeFunctions;

        public ExpressionConverter(DotsVisitFunction dotsVisitFunction, StarVisitFunction starVisitFunction, StandardFunction standardFunction,
                        StarTimeFunction starTimeFunction) {
                this.visitFunctions = ImmutableList.of(dotsVisitFunction, starVisitFunction, standardFunction);
                this.timeFunctions = ImmutableList.of(starTimeFunction, standardFunction);
        }

        public List<String> convertVisitTimeExpressionFunctional(String visitTimeExpression) {
                return Lists.newArrayList(visitTimeExpression.split(COMMA)).parallelStream().filter(it -> !it.isEmpty())
                                .map(it -> interpretSingleExpressionFunctional(it)).flatMap(it -> it.parallelStream()).collect(Collectors.toList());
        }

        public List<String> convertVisitTimeExpressionTraditional(String visitTimeExpression) {
                List<String> result = new ArrayList<String>();

                for (String singleVisitTime : visitTimeExpression.split(COMMA)) {
                        if (!singleVisitTime.isEmpty()) {
                                result.addAll(interpretSingleVisitTimeExpressionTraditional(singleVisitTime));
                        }
                }

                return result;

        }

        private List<String> interpretSingleExpressionFunctional(String singleExpression) {
                String visit = singleExpression.split(SEPARATOR)[0];
                String time = singleExpression.split(SEPARATOR)[1];
                List<String> result = Lists.newArrayList();

                visitFunctions.stream().map(it -> it.apply(visit)).flatMap(it -> it.stream()).forEach(visitIt -> {
                        timeFunctions.stream().map(it -> it.apply(time)).flatMap(it -> it.stream()).forEach(timeIt -> {
                                result.add(visitIt + SEPARATOR + timeIt);
                        });
                });
                return result;
        }

        private List<String> interpretSingleVisitTimeExpressionTraditional(String singleExpression) {

                String visit = singleExpression.split(SEPARATOR)[0];
                String time = singleExpression.split(SEPARATOR)[1];

                List<String> result = Lists.newArrayList();
                List<String> visists = Lists.newArrayList();
                List<String> times = Lists.newArrayList();

                for (Function<String, List<String>> visitFunction : visitFunctions) {
                        visists.addAll(visitFunction.apply(visit));
                }
                for (Function<String, List<String>> timeFunction : timeFunctions) {
                        times.addAll(timeFunction.apply(time));
                }
                for (String visitIt : visists) {
                        for (String timeIt : times) {
                                result.add(visitIt + SEPARATOR + timeIt);
                        }
                }
                return result;
        }
}


As you can see we have 2 public and 2 private methods, functional and traditional.

Here is a simple test focusing on the performance of the two styles :

package com.marco;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
public class ExpressionConverterTest {

        private static final String EXPRESSIONS = "6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,55..57/14400";

        private Cache cache;

        private ExpressionConverter expressionConverter;

        @Before
        public void init() {
                cache = mock(Cache.class);
                expressionConverter = new ExpressionConverter(new DotsVisitFunction(cache), new StarVisitFunction(cache), new StandardFunction(),
                                new StarTimeFunction(cache));

                Set<Integer> totVisists = Sets.newHashSet();
                for (int i = 1; i <= 100; i++) {
                        totVisists.add(i);
                }

                when(cache.getVisits(anyLong(), (BigDecimal) any(), anyInt())).thenReturn(ImmutableSet.copyOf(totVisists));
        }

        @Test
        public void testPerf() {

                long averageTraditional = 0l;
                long averageFuntional = 0l;

                for (int a = 0; a < 10; a++) {
                        long start = System.currentTimeMillis();

                        for (int i = 0; i < 1000; i++) {
                                expressionConverter.convertVisitTimeExpressionTraditional(EXPRESSIONS);

                        }
                        System.out.println("Traditional java " + (System.currentTimeMillis() - start) + " ms");
                        averageTraditional += (System.currentTimeMillis() - start);

                        long start2 = System.currentTimeMillis();
                        for (int i = 0; i < 1000; i++) {
                                expressionConverter.convertVisitTimeExpressionFunctional(EXPRESSIONS);
                        }
                        System.out.println("Functional java " + (System.currentTimeMillis() - start2) + " ms");
                        averageFuntional += (System.currentTimeMillis() - start2);
                }

                System.out.println("Average Traditional java : " + (averageTraditional / 10) + " ms");
                System.out.println("Average Functional java : " + (averageFuntional / 10) + " ms");

        }
}
 
 
And this is the output :
Traditional java 1274 ms
Functional java 773 ms
Traditional java 1054 ms
Functional java 531 ms
Traditional java 961 ms
Functional java 493 ms
Traditional java 948 ms
Functional java 492 ms
Traditional java 949 ms
Functional java 491 ms
Traditional java 958 ms
Functional java 481 ms
Traditional java 1004 ms
Functional java 474 ms
Traditional java 949 ms
Functional java 471 ms
Traditional java 947 ms
Functional java 475 ms
Traditional java 942 ms
Functional java 472 ms
Average Traditional java : 998 ms
Average Functional java : 515 ms


Functional is 2 times faster than Traditional. Why?

Because of this : parallelStream()
Parallel Stream will split the job in several tasks leveraging your multicore system.
So can you put parallelStream() everywhere and replace all the stream() methods with parallelStream() ??

No!
The parallelStream()can in fact cause a big degradation of performance if used in the wrong place.
There are loads of posts out there on this argument that should be read before using this feature, but just remember, if you want to use parallelStream() make sure to measure the performance before and after!!!!!!!!!!








 
 

Thursday, 19 June 2014

How to test circular dependencies in Spring

This will raise an exception if your dependencies have circular dependency problem

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IngesterCircularReferencesTest {

        @Test
        public void circularReferencesTest() {
                ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
                context.setAllowCircularReferences(false);
                String[] configLocations = new String[] { "/your-beans.xml" };
                context.setConfigLocations(configLocations);
                context.refresh();
                context.close();
        }
}

Tuesday, 6 May 2014

Happy customer cocktail recipe

Ingredients 


Recipe 


1 - Take a bit of Kanaban, Scrum retrospectives, XP practices and mix well.


2 - Setup your board. ( You will need an electronic board, so choose between  JIRA agile , Versionone , Leankit , etc. Remember, it's not about how big it is, but how you use it ).

3 - Setup Jenkins. You will need to build the following pipelines : 

The following to run after every commit.

Continuous build job : this is triggered every time there is a commit. It runs unit and acceptance tests against trunk.

Sonar job : If the above passed, then  it's time to check the quality of the code committed. You can use sonar and the sonar plugin for Jenkins.

The following to run every night, each depends on the success of the previous.

Continuous build job : yes again, no point in running the following if this fails.

Release jobthis creates a production release (maven release or whatever is your company policy about releases) out of your trunk(svn)/master(git) branch.


Deploy to dev job : This job automatically deploys to dev environments.

Dev System tests job : This will trigger your integration tests against dev environments.

Deploy to perf job : This job automatically deploys to performance environments.

Performance tests job : This run your JMeter tests against perf using the permormance jenkins plugin.


4 - Each business story will need to follow this process :

Pair : If the story is not trivial, do pair programming. It will speedup development and it will reduce dramatically the number of bugs.

Distill the story :  Sit with your pair, QA and the Product Owner and write down all the acceptance criteria. Ask questions here and try to cover all possible scenario.  

ATDD : Write acceptance tests first. It will not compile at first because you do not have any implementation, so write the interfaces that you need in order to make the project to compile. Run the test, it will still fail. 

TDD : Now write the test for the most external part of your code. (implement leaves first). Run the test. Implement the code to make the test pass. Test more. Implement more. Continue until the story is fully tested and implemented.

ATDD : Re run your existing acceptance tests. They should pass now.

Review : Submit the code for review. Once the code has been reviewed you can commit.

Sonar : wait for Sonar to tell you how good you and your pair are.

5 - After the Jenkins nightly pipeline is successful completed, the QA team can pick up the Release and they can deploy to their QA environment. QA will run manual, automate and exploratory tests and if bugs have been found, developers need to act immediately. 

6 - When QAs do not find bugs, the release can be deployed into UAT where the Product owners can verify and accept the stories.

7 - Once POs are happy, Ops team can deploy to Preprod in order to test the deployment scripts and the deployment instructions.

8 - Ops can deploy the release in production.

  
Happy customers can celebrate now.

 







Friday, 2 May 2014

Why you should not work extra hours

There are pros and cons in working extra hours or over time regularly, here is an attempt to list them all. Some are well known, some are taken from my experience, if you know other reasons just comment and I'll include them in the list.

CONS:


You are going to introduce bugs 


Human concentration does not last for long time, and it decreases drastically if your brain doesn't rest properly.
We are already introducing bugs when we are extremely focused and well rested in the morning, so it's easy to imagine the disaster that can happen at 10PM.
8 hours of mental work a day is more than enough for your brain.




Your changes cannot be promptly reviewed 

Code reviews is an extremely powerful tool that is widely used by team in order to control code quality. It works perfectly when reviews are done immediately and you can talk face to face with your team members. It works well when it's done using tools like Gerrit, Reviewboard and so on. It works bad if every morning there are tons of lines of code to review because of nightly or extra hours commits.






Stories team estimation goes wild


One of the most painful part of any agile team is the stories estimation (less in Kanban, more in Scrum). It is painful, stressful, long and it is tiring for all the participants. Estimations are based on stories complexity and time. The time is the 8 hours per 5 days a week. Now, all this estimation gets distorted and so useless when team members regularly work extra hours. Team will get frustrated.


You can create tension between team members


One thing I noticed in teams where some members work over time, is the presence of hostilities and tension. Why is that? People that follow regular work hours can be worried about their career being compromised because they do not stay longer in the office. People who work late can see the others as non involved/interested enough on the job. Solution? simple, stick on regular hours.




You create wrong expectations

Everyone knows, you give a hand and they want your arm. You work regularly over time and your manager will start soon to expect and to count on you to stay late.






You lose a great part of your life

Yep.














PROS:

Sunday, 27 April 2014

Software developers by nationalities

In the last 12 years I worked closely with lots of developers coming from lots of different countries.
I though to put together a post, trying to describe the commonalities and differences I found between skills and nationalities, but then I thought It would be funnier and better(I'm lazy today) to create a simple survey and ask the developers out there and so here we go:


See result on line

Thursday, 30 January 2014

Bugs types

Incompatible classes

when you try to deserialize files generated with old code...
Les trouvailles dInternet pour bien commencer la semaine #161 130114 18



























Wrong URL setting

when you put a wrong rest url in your property file and you receive no messages...
Les trouvailles dInternet pour bien commencer la semaine #160 060114 6



Too verbose xml messages

when SLAs are not met because your XML messages are too big wasting time in serialization and deserialization...
Les trouvailles dInternet pour bien commencer la semaine #162 200114 15





Production with no tests

when you go in production and you expect that everything will go fine even if you didn't test all the possible paths...

Les trouvailles dInternet pour bien commencer la semaine #162 200114 23






 

 

   

Too many requests, site is down

when you go in production without never running stress tests before...
 Les trouvailles dInternet pour bien commencer la semaine #158 161213 11



Threads bottleneck

when you spawn threads and you think it will be faster, but you forgot that synchronized method...
 Les trouvailles dInternet pour bien commencer la semaine #158 161213 20




Threads interference

when you forgot to coordinate your threads...
 Les trouvailles dInternet pour bien commencer la semaine #154 181113 25





Bug in an external library

when that external library that you choose is not behaving as expected...
 Les trouvailles dInternet pour bien commencer la semaine #153 121113 22




Wrong usage of a library

when you tried to use a library without reading the documentation...
 http://i.imgur.com/nJdP4.gif








Access private fields in unit tests

First of all, let me say out louder, you need to design your code to be testable, so you test your private fields through your public methods.

But, ("buts" are the reasons why humans are still programming instead of the computer itself, so be happy here) sometimes you want to and should alter some private fields in order to test all the possible boundaries.
Often private fields can be modified through public getter and setters or using the class constructor and in those cases the tests are easy to create and everybody is happy.
But when you use external frameworks like Spring, it may be possible that you do not have control over injected private fields.
I already explain how to mock spring components in your tests without the need of maintaining and creating ad-hoc test spring configuraitons in a previous post, here I will show you how to modify a private variable for your tests.

Let speak code:


import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.google.common.collect.ImmutableSet;
@Service
public class SomeService {

        @Value("${whitelist.api.users:A,B,C}")
        private String apiUsers;

        private ImmutableSet<String> acceptableAPIBUsers;

        @PostConstruct
        public void init() {
                acceptableAPIBUsers = ImmutableSet.copyOf(apiUsers.replaceAll(" ", "").split(","));
        }

        public boolean isAnAcceptableUser(String user) {
                return user == null ? false : acceptableAPIBUsers.contains(user.toUpperCase());
        }
}


We do not have control over  the apiUsers String, so we have couple of straightforward options, one is to create a Spring configuration for your test, modify the Spring context and mock the property, two is to create a setter to change the value of the property from your test.
I discourage from creating public assessors only for you tests, it is confusing for other people looking at your code and creating and maintaing Spring configurations for your tests can be a pain.

I know what you are thinking, "if I cannot do either of the above I'm going to get fired, my girlfriend will leave me and my life is finished", but don't you worry, I'm here to show you another option ;)



You can create a groovy class with a static method to assess your private field in your test :


import groovy.transform.CompileStatic
@CompileStatic
class SomeServiceAccessor {

        public static void setApiUsers(SomeService someService,String apiUsers){
                someService.@apiUsers = apiUsers
        }
}

And use it in your unit test:

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
public class SomeServiceTest {

        private SomeService service;

        @Before
        public void setUp() {
                service = new SomeSercvice();
                SomeSercviceAccessor.setApiUsers(service, "pippo,pluto,bungabunga");
                service.init();
        }

        @Test
        public void testIsNotApiUser() {
                assertThat(service.isAnRTBUser(""), is(false));
                assertThat(service.isAnRTBUser(null), is(false));
                assertThat(service.isAnRTBUser("random"), is(false));
        }

        @Test
        public void testIsRTBUser() {
                assertThat(service.isAnRTBUser("pippo"), is(true));
                assertThat(service.isAnRTBUser("PIPPO"), is(true));
                assertThat(service.isAnRTBUser("pluto"), is(true));
                assertThat(service.isAnRTBUser("bungabunga"), is(true));
        }
}

Of course you can do the same in java changing the visibility of the field with reflection, but I think the groovy solution can be a cleaner and easier way.

Now, I ll finish this post with the following recommendation:

Do not use this solution unless you really really really need to modify private variables to unit test your class!