How to become a better programmer?

Read this article and learn how to use declarative code has the function of a combination of a better programmer.

In many cases, declarative solutions over traditional combination of features provides the imperative code of the code. Read this article and learn how to use declarative code has the function of a combination of a better programmer.

In this article, we will examine three examples of questions and study two different technologies (imperative and declarative) to solve these problems.

All source code in this article is open source, available from https://github.com/minborg/imperative-vs-declarative get. Finally, we will also see this article to learn how to program applies to database applications. We will use Speedment Stream as an ORM tool, because it provides the database tables, views, and connect the corresponding standard Java Streams, and declarative support structure.

Indeed there are numerous examples may code for candidate metric evaluation.

1. Example of the Problem

In this article, I chose three common problems developers may encounter in their daily work:

1.1.SumArray

Iterative array and perform calculations

1.2.GroupingBy

Parallel aggregate value

1.3.Rest

Use paging implement REST interface

2. Solutions Technology

As described in the beginning of this article, we will use both coding technology to solve the problem:

2.1 Command solutions

A command-based solution, we use the traditional code samples for loops and with explicit state variable.

2.2 declarative solutions

Declarative solution where we have a combination of various functions to form the composite higher-order function to solve the problem, usually java.util.stream.Stream, or a variant thereof.

3. Code Index

However, our idea is to use SonarQube (here SonarQube Community Edition, Version 7.7) will be applied to different static code analysis solution, so that we can be a problem / solution portfolio to derive useful and standardized code metrics. Then compare these indicators.

In this article, we will use the following code metrics:

3.1. PLACE

"LOC" means "line" is the number of non-null line of code.

3.2. Statements

The total number of statements in the code. You may have zero to more than one statement on each line of code.

3.3. Complexity cycle

It represents the complexity of the code, and is quantified by the number of linearly independent paths metrics source code. For example, a single "if" clause shows two separate paths in the code. Read more on Wikipedia content .

3.4. Cognitive complexity

SonarCube stated:

"Cognitive complexity has changed the practice of using mathematical models to assess the maintainability of the software. It starts Cyclomatic Complexity set a precedent, but use human judgment to assess how the structure should be calculated, and decide what should be added to the model as a whole result, it produces a fraction of the complexity of the method, which allows the programmer to evaluate the maintainability model of more equitable than before. "

On SonarCube own page you can read more content .

Under normal circumstances, we need to imagine a solution where these indicators is very small and not very big.

For the record, it should be noted that following the design of any solution is just a way to solve any given problem. If you know a better solution, please feel free to https://github.com/minborg/imperative-vs-declarative pulling request to submit comments.

4. iterative array

Let's start simple. Object of this calculation example of the problem is the sum of the elements of the array int, and returns the result to a long. The following interface defines the problem:

public interface SumArray {
    long sum(int[] arr);
}

4.1. Imperative Solutions

The following solution uses technology SumArray imperative question:

public class SumArrayImperative implements SumArray {
    @Override
    public long sum(int[] arr) {
        long sum = 0;
        for (int i : arr) {
            sum += i;
        }
        return sum;
    }
}

4.2 declarative solutions

This is a declarative technology SumArray solution:

public class SumArrayDeclarative implements SumArray {
    @Override
    public long sum(int[] arr) {
        return IntStream.of(arr)
            .mapToLong(i -> i)
            .sum();
    }
}

Please note, IntStream :: sum returns only one int, therefore, we must join the intermediate operating mapToLong ().

4.3 Analysis

SonarQube provides the following analysis:

image

image

SumArray code metric table below (generally lower):

technology PLACE Statements The complexity of the cycle Cognitive complexity
Imperative 12 5 2 1
Functional 11 2 2 0

This is its value (generally lower) in the diagram:

image

5. Parallel aggregate value

The object of this example is to issue Person objects grouped into different buckets, where each bucket constitute a unique combination of a person's year of birth and the country of a person's work. For each group, the average salary should be calculated. The polymerization should be in the public pool ForkJoin parallel computing.

This is the (immutable) the Person class:

public final class Person {
    private final String firstName;
    private final String lastName;
    private final int birthYear;
    private final String country;
    private final double salary;
    public Person(String firstName, 
                  String lastName, 
                  int birthYear, 
                  String country, 
                  double salary) {
        this.firstName = requireNonNull(firstName);
        this.lastName = requireNonNull(lastName);
        this.birthYear = birthYear;
        this.country = requireNonNull(country);
        this.salary = salary;
    }
    public String firstName() { return firstName; }
    public String lastName() { return lastName; }
    public int birthYear() { return birthYear; }
    public String country() { return country; }
    public double salary() { return salary; }
    // equals, hashCode and toString not shown for brevity
}

We also define another class called YearCountry immutable, and the group key as:

public final class YearCountry {
    private final int birthYear;
    private final String country;
    public YearCountry(Person person) {
        this.birthYear = person.birthYear();
        this.country = person.country();
    }
    public int birthYear() { return birthYear; }
    public String country() { return country; }
    // equals, hashCode and toString not shown for brevity
}

After these two classes defined, we can now define an example of this problem through the interface:

public interface GroupingBy {
    Map<YearCountry, Double> average(Collection<Person> persons);
}

5.1. Imperative solution

Order to achieve GroupingBy example problem solutions will not be easy. This is a solution to the problem:

public class GroupingByImperative implements GroupingBy {
    @Override
    public Map<YearCountry, Double> average(Collection<Person> persons) {
        final List<Person> personList = new ArrayList<>(persons);
        final int threads = ForkJoinPool.commonPool().getParallelism();
        final int step = personList.size() / threads;
        // Divide the work into smaller work items
        final List<List<Person>> subLists = new ArrayList<>();
        for (int i = 0; i < threads - 1; i++) {
           subLists.add(personList.subList(i * step, (i + 1) * step));
        }
        subLists.add(personList.subList((threads - 1) * step, personList.size()));
        final ConcurrentMap<YearCountry, AverageAccumulator> accumulators = new ConcurrentHashMap<>();
        // Submit the work items to the common ForkJoinPool
        final List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < threads; i++) {
            final List<Person> subList = subLists.get(i);
       futures.add(CompletableFuture.runAsync(() -> average(subList, accumulators)));
        }
        // Wait for completion
        for (int i = 0; i < threads; i++) {
            futures.get(i).join();
        }
        // Construct the result
        final Map<YearCountry, Double> result = new HashMap<>();
        accumulators.forEach((k, v) -> result.put(k, v.average()));
        return result;
    }
    private void average(List<Person> subList, ConcurrentMap<YearCountry, AverageAccumulator> accumulators) {
        for (Person person : subList) {
            final YearCountry bc = new YearCountry(person);
          accumulators.computeIfAbsent(bc, unused -> new AverageAccumulator())
                .add(person.salary());
        }
    }
    private final class AverageAccumulator {
        int count;
        double sum;
        synchronized void add(double term) {
            count++;
            sum += term;
        }
        double average() {
            return sum / count;
        }
    }
}

5.2. Declarative Solution

This is a declarative structure to achieve GroupingBy solution:

public class GroupingByDeclarative implements GroupingBy {
    @Override
    public Map<YearCountry, Double> average(Collection<Person> persons) {
        return persons.parallelStream()
            .collect(
             groupingBy(YearCountry::new, averagingDouble(Person::salary))
            );
    }
}

In the above code, I used some static imports from Collectors class (for example Collectors :: groupingBy). This does not affect the code index.

5.3 Analysis

SonarQube provides the following analysis:

image

image

GroupingByCode metric table below (generally lower):

technology PLACE Statements The complexity of the cycle Cognitive complexity
Imperative 52 27 11 4
Functional 17 1 1 0

This is its value (generally lower) in the diagram:

image

6. Implement a REST interface

In this example problem, we will provide paging services Person object. Persons appear on the page must satisfy certain (optional) condition, press a specific order. This page will be returned as unmodifiable list of Person objects.

This is a problem of the interface:

public interface Rest {

/**

 * Returns an unmodifiable list from the given parameters.
 *
 * @param persons as the raw input list
 * @param predicate to select which elements to include
 * @param order in which to present persons
 * @param page to show. 0 is the first page
 * @return an unmodifiable list from the given parameters
 */

 List<Person> page(List<Person> persons, 
                   Predicate<Person> predicate,
                   Comparator<Person> order,
                   int page);
}

The size of the page in a separate utility class named RestUtil in:

public final class RestUtil {
    private RestUtil() {}
    public static final int PAGE_SIZE = 50;
}

6.1. Imperative Implementation

public final class RestImperative implements Rest {
    @Override
    public List<Person> page(List<Person> persons, 
                Predicate<Person> predicate, 
                  Comparator<Person> order, 
                             int page) {
        final List<Person> list = new ArrayList<>();
        for (Person person:persons) {
            if (predicate.test(person)) {
                list.add(person);
            }
        }
        list.sort(order);
        final int from = RestUtil.PAGE_SIZE * page;
        if (list.size() <= from) {
            return Collections.emptyList();
        }
        return unmodifiableList(list.subList(from, Math.min(list.size(), from + RestUtil.PAGE_SIZE)));
    }
}

6.2. Declarative Solution

public final class RestDeclarative implements Rest {
    @Override
    public List<Person> page(List<Person> persons,
                      Predicate<Person> predicate, 
                        Comparator<Person> order,
                             int page) {
        return persons.stream()
            .filter(predicate)
            .sorted(order)
            .skip(RestUtil.PAGE_SIZE * (long) page)
            .limit(RestUtil.PAGE_SIZE)
           .collect(collectingAndThen(toList(), Collections::unmodifiableList));
    }
}

6.3 Analysis

SonarQube provides the following analysis:

image

image

RestCode metric table below (generally lower):

technology PLACE Statements The complexity of the cycle Cognitive complexity
Imperative 27 10 4 4
Functional 21 1 1 0

This is its value (generally lower) in the diagram:

image

7.Java 11 improvements

The example above is written in Java 8. Use Java 11, we can use LVTI (local variable type inference) to shorten the declarative code. This makes our code shorter, but will not affect the code index.

@Override
public List<Person> page(List<Person> persons,
                         Predicate<Person> predicate, 
                         Comparator<Person> order, 
                         int page) {
    final var list = new ArrayList<Person>();
    ...

Compared with Java 8, Java 11 contains a number of new collectors. For example, Collectors.toUnmodifiableList(), it will make our solutions more declarative Rest shorter:

public final class RestDeclarative implements Rest {
@Override
public List<Person> page(List<Person> persons,
                         Predicate<Person> predicate, 
                         Comparator<Person> order, 
                         int page) {
    return persons.stream()
        .filter(predicate)
        .sorted(order)
        .skip(RestUtil.PAGE_SIZE * (long) page)
        .limit(RestUtil.PAGE_SIZE)
        .collect(toUnmodifiableList());
}

Again, this will not affect the code index.

8. Summary

Code three exemplary average measurement result in the following problems (generally lower):

image

Given the input requirements herein, when we constructed from imperative to declarative configuration, all code metrics are significantly improved.

8.1. Declarative configuration in a database application

In order to obtain the benefits of declarative structure of the database application, we use Speedment Stream . Speedment Stream is a stream-based Java ORM tool can be any database table / view / connection to Java stream, allowing you to use declarative skills in database applications.

Your database application code will become better. In fact, for Speedment database REST and Spring Boot paging solution may be expressed as follows:

public Stream<Person> page(Predicate<Person> predicate, 
                     Comparator<Person> order, 
                           int page) {
    return persons.stream()
        .filter(predicate)
        .sorted(order)
        .skip(RestUtil.PAGE_SIZE * (long) page)
        .limit(RestUtil.PAGE_SIZE);
}

Manager<Person> personsBy Speedment, and constitute a "Person" handle database tables, you can use @AutoWired annotation by Spring.

9. summary

Select the command declarative solutions can greatly reduce the general code complexity, and can provide many benefits, including faster encoding, better code quality, more readable, less testing, lower maintenance costs and many more.

In order to benefit from the declarative configuration database applications in, Speedment Stream is a standard Java Streams can provide tools directly from the database.

Master declarative structure and function of a combination of any of today's modern Java developer required.


August welfare struck on time, public concern number backstage Re: July 003 can receive a translation of highlights oh go on welfare Re: 001, 002, you can receive!



img

Guess you like

Origin www.cnblogs.com/liululee/p/11444588.html