この記事を読み、宣言型のコードはより良いプログラマの組み合わせの機能を持って使用する方法を学びます。
多くの場合、機能の伝統的な組み合わせを超える宣言型のソリューションは、コードの不可欠コードを提供します。この記事を読み、宣言型のコードはより良いプログラマの組み合わせの機能を持って使用する方法を学びます。
この記事では、私たちが質問の3つの例を検討し、これらの問題を解決するには、2つの異なる技術(不可欠と宣言型)を検討します。
この記事のすべてのソースコードは、から入手できるオープンソース、ですhttps://github.com/minborg/imperative-vs-declarative取得。最後に、我々はまた、データベースアプリケーションに適用されるプログラミング方法を学ぶためにこの記事が表示されます。私たちは、使用するSpeedmentストリームを、それがデータベーステーブル、ビューを提供するので、ORMツールとして、および対応する標準Javaストリームを接続し、宣言型の支持構造。
実際に候補メトリック評価をコードすることができる多数の例があります。
問題の1例
この記事では、私は、開発者が日常業務で発生する可能性がある3つの共通の問題を選択しました:
1.1.SumArray
反復配列と計算を実行
1.2.GroupingBy
パラレル集計値
1.3.Rest
RESTインターフェイスを実装ページングを使用します
2.ソリューションテクノロジー
この記事の冒頭で述べたように、我々は問題を解決するために、両方の符号化技術を使用します。
2.1コマンド・ソリューション
コマンドベースのソリューションは、我々はforループと、明示的な状態変数を用いて従来のコードサンプルを使用します。
2.2宣言型ソリューション
宣言私たちは通常、問題を解決するために複合高次関数を形成するために、様々な機能の組み合わせを有する溶液java.util.stream.Stream
、又はその変異体。
3.コードのインデックス
しかし、私たちのアイデアは、私たちが便利で標準化されたコードメトリクスを導出する問題/ソリューション・ポートフォリオできるように、異なる静的コード解析ソリューションに適用されますSonarQube(ここでSonarQubeコミュニティ版、バージョン7.7)を使用することです。そして、これらの指標を比較します。
この記事では、次のコードメトリクスを使用します。
3.1。LOC
「LOC」と「線」コードの非ヌル行の数です。
3.2。ステートメント
コード内のステートメントの総数。あなたは、コードの各ライン上に複数の文にゼロを有することができます。
3.3。複雑サイクル
これは、コードの複雑さを表し、且つ線形独立パスメトリックのソースコードの数によって定量化されます。例えば、単一の句は、コード内の2つの別個の経路を示す「IF」。ウィキペディアについて詳しく読んで内容を。
3.4。認知複雑
SonarCubeは、次のように述べました。
「認知の複雑さは、それは循環的複雑度は、先例を開始します。ソフトウェアの保守性を評価するための数学的モデルを使用しての練習を変更しましたが、構造が計算されるべきであるかを評価し、モデルなどを追加すべきかを決定するために人間の判断を使用しています全体の結果は、それはプログラマが以前よりもより公平の保守性モデルを評価することを可能にする方法の複雑さの割合を生成します。」
SonarCube自身のページでは、より多く読むことができるコンテンツを。
通常の状況下で、我々はこれらの指標は非常に大きな非常に小さく、ではないソリューションを想像する必要があります。
記録のために、どんなソリューションの設計を以下は任意の与えられた問題を解決するだけの方法であることに留意すべきです。あなたがよりよい解決策を知っている場合は、気軽にしてくださいコメントを提出する要求を引っ張っhttps://github.com/minborg/imperative-vs-declarative。
4.反復配列
のは、簡単な始めましょう。この問題の計算例の目的は、配列intの要素の和であり、長に結果を返します。次のインタフェースは、問題を定義します。
public interface SumArray {
long sum(int[] arr);
}
4.1。命令型ソリューション
次のソリューションは、技術SumArray不可欠質問を使用しています。
public class SumArrayImperative implements SumArray {
@Override
public long sum(int[] arr) {
long sum = 0;
for (int i : arr) {
sum += i;
}
return sum;
}
}
4.2宣言型ソリューション
これは、宣言の技術SumArrayソリューションです:
public class SumArrayDeclarative implements SumArray {
@Override
public long sum(int[] arr) {
return IntStream.of(arr)
.mapToLong(i -> i)
.sum();
}
}
)そのため、我々は(中間運転mapToLongに参加する必要があり、IntStreamは::合計が唯一のintを返し、注意してください。
4.3分析
SonarQubeは、以下の分析を提供します。
以下SumArrayコードメトリックテーブル(一般的に低いです)。
テクノロジー | LOC | ステートメント | サイクルの複雑さ | 認知複雑 |
---|---|---|---|---|
命令 | 12 | 5 | 2 | 1 |
機能の | 11 | 2 | 2 | 0 |
これは、図中の値(一般的に低い)です。
5.パラレル集計値
この例の目的は、各バケットは、出生の人の年のユニークな組み合わせと人の仕事の国を構成する別のバケットにグループ化Personオブジェクトを発行することです。各グループについて、平均給与を計算しなければなりません。重合は、並列計算ForkJoin公共のプールにあるべきです。
これは、(不変)Personクラスです。
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
}
我々はまた、不変YearCountryと呼ばれる別のクラス、およびグループキーを定義します。
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
}
これら2つのクラスが定義された後、我々は今、インターフェイスを介してこの問題の例を定義することができます。
public interface GroupingBy {
Map<YearCountry, Double> average(Collection<Person> persons);
}
5.1。命令型ソリューション
GroupingBy例の問題の解決を達成するためには、容易ではないだろう。これは、問題を解決します:
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。宣言型ソリューション
これはGroupingByソリューションを達成するための宣言型の構造であります:
public class GroupingByDeclarative implements GroupingBy {
@Override
public Map<YearCountry, Double> average(Collection<Person> persons) {
return persons.parallelStream()
.collect(
groupingBy(YearCountry::new, averagingDouble(Person::salary))
);
}
}
上記のコードでは、私はコレクタークラスのいくつかの静的インポート(例えばコレクター:: groupingBy)を使用しました。これは、コードインデックスには影響を与えません。
5.3分析
SonarQubeは、以下の分析を提供します。
GroupingBy
以下(一般的に低い)コードメトリック表:
テクノロジー | LOC | ステートメント | サイクルの複雑さ | 認知複雑 |
---|---|---|---|---|
命令 | 52 | 27 | 11 | 4 |
機能の | 17 | 1 | 1 | 0 |
これは、図中の値(一般的に低い)です。
6. RESTインタフェースを実装
この例題では、我々は、ページングサービスにPersonオブジェクトを提供します。人は、特定の順序を押して、特定の(オプション)の条件を満たさなければならないページに表示されます。このページでは、Personオブジェクトの変更不可能なリストとして返されます。
これは、インターフェイスの問題です。
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);
}
でRestUtilという名前の別のユーティリティクラスのページのサイズ:
public final class RestUtil {
private RestUtil() {}
public static final int PAGE_SIZE = 50;
}
6.1。命令型実装
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。宣言型ソリューション
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分析
SonarQubeは、以下の分析を提供します。
Rest
以下(一般的に低い)コードメトリック表:
テクノロジー | LOC | ステートメント | サイクルの複雑さ | 認知複雑 |
---|---|---|---|---|
命令 | 27 | 10 | 4 | 4 |
機能の | 21 | 1 | 1 | 0 |
これは、図中の値(一般的に低い)です。
7.Java 11の改良
例は上記のJava 8で記述されています。Javaの11を使用して、我々は、宣言型のコードを短くするLVTI(ローカル変数の型推論)を使用することができます。これは、我々のコードが短くなりますが、コードインデックスには影響しません。
@Override
public List<Person> page(List<Person> persons,
Predicate<Person> predicate,
Comparator<Person> order,
int page) {
final var list = new ArrayList<Person>();
...
Javaの8と比較すると、Javaの11は、新たなコレクターの数が含まれています。例えば、Collectors.toUnmodifiableList
()、それが当社のソリューションは、より宣言休憩短くなります。
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());
}
繰り返しますが、これはコードインデックスには影響しません。
8.まとめ
コードは次のような問題点(一般的に低い)における3つの例示的な平均測定結果:
私たちは、宣言の構成に必要不可欠から構築する場合、本明細書の入力要件を考えると、すべてのコードメトリックは大幅に改善されています。
データベースアプリケーション8.1。宣言型構成
データベースアプリケーションの宣言型構造の利点を得るために、我々は、使用Speedmentストリームを。Speedmentストリームは、ストリームベースのJava ORMツールを使用すると、データベースアプリケーションで宣言型のスキルを使用することができ、Javaストリームへの任意のデータベーステーブル/ビュー/接続することができます。
データベースアプリケーションのコードが良くなります。次のように実際には、SpeedmentデータベースRESTとSpringブートページングソリューションを表現することができます。
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> persons
Speedment、そして「人」のハンドルデータベーステーブルを構成することで、あなたは春で@Autowiredアノテーションを使用することができます。
9.概要
宣言型のソリューションが大幅に一般的なコードの複雑さを軽減することができ、かつ高速エンコーディング、より良いコードの品質、読みやすく、より少ないテスト、低維持費を含む多くの利点を提供することができるコマンドを選択しますように。
で宣言型構成データベースアプリケーションから利益を得るためには、Speedmentストリームは、標準のJavaストリームは、データベースから直接ツールを提供することができます。
必要に今日の近代的なJava開発者のいずれかの組み合わせの宣言型構造と機能をマスター。
8月の福祉は時間に打たれ、国民の関心番号舞台裏日時:7月003は、オハイオ州のハイライトの翻訳受け取ることができる福祉の日時に行く:001、002、あなたが受け取ることができます!