Java

2022/11/20

ComparableとComparatorの違いは何か

ComparableとComparatorの違いを混同していた。
自分用のラフなメモ書き。

【参考】
Java:任意の順番でのソート ? サイゼントの技術ブログ

compareToでJavaのソートは自由自在! 一からお伝えします

Java - オブジェクトの比較(==、equals、Comparable、Comparator)

Java Comparatorメモ(Hishidama's Java Comparator Memo)


結論は、
java.lang.Comparableは、Arrays.sort(Object[]), Collections.sort(List)で使う。
ソートを行いたいクラスにComparatorを明示的に実装する場合に使われる。

java.util.Comparatorは、ラムダ式やstream.sorted()で使う。
Comparatorは、ソートしたいクラスからソートのロジック部分を分離したいときに使われる。

違いは、Comparatorインターフェイスではなく、Comparatorはオブジェクトとして作成する。

class Book implements Comparable{} は、
処理の中で自分のクラス(Book)のソート順を定義します。
comparaTo()をオーバーライドして定義します。

//java.lang.Comparableは、自分のクラスと比較してソートする
public class Book implements Comparable{
// タイトル
private String title;
// 値段
private int price;
// 発行日
private LocalDate dateOfIssue;
@Override
public int compareTo(Book book){
return title.compareTo(book.title);
}
}
Collections.sort(bookList);

つまり、Comparableを格納しているListをソートする際は、Collections.sort(List)/reverse(List)を使うのが基本。
AA.compareTo(BB) => java.lang.Comparable を連想する。

たとえば、String#compareTo(String)、Integer#compareTo(Integer)になる。
Comparable.compareToのコツは、判断結果は小さい・等しい・大きいの3種類だけ。
値や文字列が等しいか、どちらが大きいかを判断するだけ。

オブジェクトや文字列や数値のListやSetを直接ソートしたい場合は、Comparableを継承して、compareToメソッドを実装する。
その方が楽。

一方、class BookTitleComparator(ソート用クラス) implements Comparator{} は、
処理の中で何かのクラス(ソートさせたいクラス)のソート順を定義します。
compare()をオーバーライドして定義します。

//java.util.Comparatorは、任意のクラスをソートできる
public class BookTitleComparator implements Comparator {
@Override
public int compare(Book book1, Book book2){
return book1.getTitle().compareTo(book2.getTitle());
}
}
bookList.sort(new BookTitleComparator());
または
bookList.sort((book1, book2) -> book1.getTitle().compareTo(book2.getTitle()));

ストリームAPIでは、基本はComparatorを使う場合が多い。
なぜなら、ソート処理にラムダ式を頻繁に使うから。

こんなことを調べてみると、ComparableはComparatorよりも古いAPIなのだろう。
Java8以後でラムダ式が生まれたので、Comparatorはその後に定義されたのだろう。

実際、java.lang.Comparableなので、import文は不要。
一方、java.util.Comparatorなので、必ずimport文は必要になる。

| | コメント (0)

Javaのsumはreduceで置き換えられる

JavaのストリームAPIとして、sum()とreduce()をよく混同していた。
JDKのsum()の定義を読んだら、Javaのsumはreduceで置き換えられると分かった。
以下は自分用のラフなメモ書き。

IntStream (Java SE 11 & JDK 11 )

Javaのreduceの使い方は2種類ある: プログラマの思索

Java Optionalメモ(Hishidama's Java8 Optional Memo)

一般に、sumはこんな感じで使われる。
int total = items.stream().mapToInt(Item::getPrice).sum();

sumはIntStreamだけで定義されているのに、Streamでも使えると混乱していた。
だから、map()の後に使えるのか、mapToInt()の後に使えるのか混乱していた。
理由は、数値配列型のStreamでしか使えないからだろう。

もう一つは、sumの戻り値がなぜOptional型ではないのか?
averageの戻り値はOptionalDoubleだし、他の統計処理メソッドもOptionalが多いのに、なぜsumだけがint型が許されるのか?

(引用開始)
sum
int sum()
このストリーム内の要素の合計を返します。 これはリダクションの特殊な場合であり、次と同等になります。

return reduce(0, Integer::sum);

これは終端操作です。

戻り値:
このストリームの要素の合計
(引用終了)

上記の定義を読むと、sumは、 return reduce(0, Integer::sum); と同じなので、必ず初期値0が設定されているので、NULLになる可能性はなく、Optional型はなくてもいい。
sumの戻り値の型はIntegerが正しいが、オートボクシングされてint型もコンパイルOKになる。

つまり、
int total = items.stream().mapToInt(Item::getPrice).reduce(0, Integer::sum);

int total = items.stream().mapToInt(Item::getPrice).sum();
は同じ。

sumをreduceで書き換える時、こんな感じでもOK。

int total = items.stream().map(Item::getPrice).reduce((x,y) -> x+y).orElse(0);
int total = items.stream().map(Item::getPrice).reduce((x,y) -> x+y).get();
int total = items.stream().map(Item::getPrice).reduce(0, (x,y) -> x+y);

reduce(op)の戻り値はOptionalになる。
だから、Optional演算子orElse, getが必要。

一方、reduce(0, op)の戻り値はIntegerになる。
だから、戻り値はオートボクシングされてint型もOK。

IntStream.mapToInt(ToIntFunction).sum()で覚える。
Stream.map(Function,Optional>).reduce(bo)で覚える。

ちょっとややこしいのは、averageの場合、戻り値=OptionalDoubleになる。
なぜならば、DoubleStreamでaverageが定義されているから。
Optionalではない点に注意。

DoubleStream (Java SE 11 & JDK 11 )

こんな例かな。
double avg = items.stream().mapToInt(Item::getPrice).average().getAsDouble();
double avg = items.stream().mapToInt(Item::getPrice).average().orElse(0.0);

OptionalDoubleをOptionalへ変換できるか?という記事もあった。

Convert OptionalDouble to Optional - Stack Overflow

下記の記事が分かりやすい。
JavaのOptionalは、ScalaのOptionクラスに相当する。
Optional型で扱えば、空かどうかの判定を自動判定してくれるメリットがある。

Java Optionalメモ(Hishidama's Java8 Optional Memo)

「OptionalとOptionalInt等の相互変換は出来ないようだ。(継承関係も無いし、変換に使えそうなメソッドも無い)」とのことなので、OptionalとOptionalは明確に異なる。
たぶん、IntStreamの場合とStreamの場合で異なるのだろう。

| | コメント (0)

2022/11/14

JavaのモジュールシステムでSPIとDIを実現するやり方

JavaGold黒本を読んでいたら、JavaのモジュールシステムでSPIとDIを実現するやり方をやっと理解できた。
理解できたことをラフなメモ。

【参考】
Javaのモジュールシステムの考え方をまとめてみた: プログラマの思索

クラウド上の開発がJavaに与えた影響は何なのか: プログラマの思索

Javaのモジュールシステムは複雑性をより増している: プログラマの思索

Java 9のモジュール機能「サービス(SPI)」と既存ライブラリの共存 (1/2)|CodeZine(コードジン)

DI(依存性の注入)とは依存性を注入するということである、、? - Qiita

module-info.javaに下記のような記述がある。
Sampleモジュールはどんな構造を持っているのか?

module Sample {
exports test;
uses test.Hello;
}

Sampleモジュールは、testパッケージを公開しており、test.Helloインターフェイスの実装クラスを利用する。
つまり、Sample単体ではコンパイルできるけれど、実装クラスがセットでなければ動作しない。

SPI(Service Provider Interface)を実装したHelloモジュールがない場合、コンパイルエラーにならない。
実際、ServiceLoader.load()は空のServiceLoaderインスタンスを返却するだけでスキップされる。
こんなプログラムになるだろう。

ServiceLoader loader = ServiceLoader.load(Hello.class);
Iterator iter = loader.iterator();
while (iter.hasNext()) {
Hello obj = iter.next();
System.out.println(obj.sayHello());
}

つまり、META-INF/services/helloというテキストファイルから読み込む時に、実装クラスが書いてなければ、ループ処理は空回りするだけ。

では、test.Helloインターフェイスの実装クラスtest.impl.HelloImplを持つHelloモジュールはどんな構造を持つのか?
たぶんこんな構造だろう。

module Hello {
exports test.impl;
requires Sample;
provides test.Hello with test.impl.HelloImpl;
}

Helloモジュールは、test.implパッケージを公開して、Helloの実装クラスHelloImplを提供する。
この時、HelloモジュールがSampleモジュールを呼び出し、Sampleへの依存関係を持つ点が重要。
なぜならば、test.Helloを利用するには、定義されているSampleモジュールをImportする必要があるから。

すると、SampleとHelloは依存性の注入(DI:Dependency Injection)を実現していることになる。
なぜならば、SPIの定義より、クライアント側Sampleはライブラリ側Helloの実装内容を利用しているのに、プログラムの依存関係としては、HelloからSampleへ逆向きの依存関係を持っているからだ。

つまり、普通は、Sample(利用側) --> Hello(提供側) になるはずだが、Sample(利用側) <-- Hello(提供側) なので、依存関係が逆転している。

まとめると、
Sample --> testパッケージ --> test.Helloインターフェイス
Hello --> Sample --> test.Helloインターフェイス
Hello --> test.impl.HelloImplクラス
という依存関係を持つ。

DIが実現されていることによってどんなメリットがあるのか?

まず、test.Helloインターフェイスを実装した具象クラスは、Sampleモジュールのモジュールパスに含める必要はない。
なぜならば、SPIの実装は後から提供されるので、実装に依存する必要はないからだ。
つまり、最初にIFだけ定義して公開しておけば、SPIの実装側ライブラリは後から実装すればいい。
IFという約束事だけ定義して、具体的な中身は後から開発できるので、開発スケジュールを調整しやすく、仕様変更にも柔軟に対応できる。

次に、test.Helloインターフェイスを実装するモジュールは、Sampleモジュールを再コンパイルする必要はない。
なぜならば、SPIの拡張ポイントとなるインターフェイスは変わらない限り、実装クラスに依存しないからだ。
つまり、SPIを利用する側のSampleモジュールと、実装クラスを提供するHelloは独立してコンパイルできる。

CやJavaなどでは、リプレース時にリコンパイルが必要だったりして、開発環境を準備したり、リリースするときは割と面倒だが、リコンパイルが不要であれば、過去にコンパイルしたモジュールをそのまま利用すればいい。

このモジュールシステムのサンプルから、僕はJDBCドライバの実装を連想した。
JDBCドライバでは、JDBCドライバのIFは既に定義されているが、各RDBごとにJDBCドライバの実装は異なる。
つまり、ユーザ側はJDBCドライバのIFを使ってプログラムを組めば良く、各RDBの実装の違いを意識する必要はない。
後から、利用するRDBのJDBCドライバを組み込めばいい。

2000年代にDIのアイデアが出た時はすごい設計技法のようなイメージだったが、2020年代の今となっては枯れた技術の一つにすぎない。
しかし、こういう依存関係を逆転する技術を利用して、プログラムの複雑性を手懐けようとする設計手法は、今後もその必要性は変わらないと思う。

| | コメント (0)

2022/11/13

Javaのreduceの使い方は2種類ある

Javaのreduceの戻り値が異なるのに戸惑っていた。
Javaのreduceの使い方は2種類ある。
ラフなメモ。

【参考】
Java:Streamのreduceの使用方法 | GWT Center

Stream BinaryOperator(Java Platform SE 8 )


Javaのreduceは、Rubyのreduceみたいに、累積する処理みたいなもの。
MapRedcueの間柄のように、map関数とreduce関数はペアで考える。

reduceの引数はBinaryOperator型になるのは分かる。
合計値のように、2つの引数を累積させる処理を行って、1つの戻り値を返すだけ。

しかし、reduce(BinaryOperator)とreduce(初期値, BinaryOperator)は戻り値の型が異なるのに混同していた。
結論は、初期値を設定していなければ、戻り値が空の場合があるのでOptionalでラッピングして返す。
Optionalであれば、NullPointerExceptionのリスクを無くせるメリットがある。

reduceの引数に初期値を設定していれば、戻り値がなくても初期値をそのまま返せばいい。
つまり、戻り値はプリミティブ型でも問題ない。

Java:Streamのreduceの使用方法 | GWT Centerの説明がわかりやすかった。
以下引用している。

【パターン1】Optional reduce(BinaryOperator accumulator)
戻り値がない場合があるので、Optionalで返す。

// 何らかの要素があったか
boolean foundAny = false;
// 最終結果
T result = null;
for (T element : このストリームの要素を列挙する) {
if (!foundAny) {
// 最初の要素の場合
foundAny = true;
result = element;
} else {
// 二番目以降の要素の場合
result = accumulator.apply(result, element);
}
}
// 結果があればそれを返す。なければ空を返す
return foundAny ? Optional.of(result) : Optional.empty();

【パターン2】T reduce(T identity, BinaryOperator accumulator)

初期値が与えられているので「値が存在しない」ということは無く、戻り値はOptionalではない。

// 初期値
T result = identity;
for (T element : このストリームの要素を列挙する) {
// 初期値に加算していく
result = accumulator.apply(result, element);
}
return result;

IntStream (Java SE 11 & JDK 11 )には、reduceの考え方が書かれている。
これは参考になる。

int reduce​(int identity, IntBinaryOperator op)
指定された単位元の値と結合的な累積関数を使ってこのストリームの要素に対してリダクションを実行し、リデュースされた値を返します。 これは、次の操作に相当します。

int result = identity;
for (int element : this stream)
result = accumulator.applyAsInt(result, element)
return result;

つまり、reduceは何らかのアルゴリズムに添って累積させる処理を行っている。
その累積処理は抽象化されているので、アルゴリズムにラムダ式を当てはめれば汎用的に使えるわけだ。

| | コメント (0)

2022/10/21

Javaのモジュールシステムの考え方をまとめてみた

Javaのモジュールシステムの理解が深まったのでメモ。
Java初心者のラフなメモ書き。

【1】モジュールシステムはなぜJavaで必要なのか?

異なるJarであっても、同一パッケージ名が衝突する問題があった。
モジュールは、パッケージを区別するための仕組み。
パッケージはクラスを包み込み、モジュールはパッケージを包み込む。

Javaはオブジェクト指向言語なので、機能追加したい場合、開放閉鎖原則に従って、既存クラスは修正せず新規クラスを追加する。
Rubyのオープンクラスみたいなもの。
すると、クラスがどんどん増えるので、パッケージでクラスを分類しようとする。
そして、パッケージをまとめたJarを配布して、開発者に利用してもらうようにする。

しかし、Jarファイルもどんどん増えてしまって、異なるJarなのに同一パッケージで衝突する場合がある。
Mavenでこういう依存ライブラリのJarを管理するけれど、名前衝突が多くなるのだろう。

【2】なぜ、無名モジュール、自動モジュールは必要なのか?

Java9以後はモジュールシステムを使う必要があるが、以前のJara8のJarファイルはモジュールに対応していない。
Java8のJarファイルを利用できるような環境としてモジュールが導入された。

基本は、Classpathにある無名モジュールが一般的だが、modulepathにないので、名前付きモジュールから無名モジュールを読み込めない。
そこで、modulepathにJarファイルを置いて、自動モジュールに変更して、名前付きモジュールから自動モジュールを読み込めるようにした。

実際の現場を見ると、Java8までで止まっているWebシステムは割と多いように感じる。
たぶん、Java9以後のモジュールシステムに対応するように、移植するのは難しい場面があるからだろう。

【3】無名モジュール、自動モジュールとは何か?

無名モジュールはJava9以前のJarで、Classpathにある。
以前のコマンドみたいに、java -cp **.jarみたいに使う。

なお、無名モジュールのJarはclasspathにあるので、名前付きモジュールから読み込めない。

自動モジュールはJava9以前のJarで、ModulePathにある。
java --module-path **.jar とか、java -p **.jarみたいに使う。

名前付きモジュール--> 自動モジュール、自動モジュール --> 名前付きモジュールの両方で読み込める。
なぜならば、modulepathに名前付きモジュールも自動モジュールも両方配置されているから。

なお、無名モジュールも自動モジュールも、パッケージのクラスは全て公開されているから、モジュールシステムのように公開の制限はできない。

無名モジュールはJDK9以前のライブラリ。
module-info.javaもMETA-INF/MANIFEST.MFもない。
ClasspathにJarを配置していると、java --module-path **.jarが使えない。

自動モジュールは、META-INF/MANIFEST.MFにAutomatic-Module-Name属性が指定されている or jarファイル。
JDK9以後は、java -p **.jarで実行する。

自動モジュールはできるだけMETA-INF/MANIFEST.MFにAutomatic-Module-Name属性を指定する。
なぜならば、Jarファイル名は書き換えられやすいので間違いやすい。
META-INF/MANIFEST.MFに記述すれば、Jarファイル名が書き換えられても呼び出されるJarは同じになるので安全。

【4】jlinkはなぜ必要なのか?

専用のランタイム用アプリを作りたい。
JDK9以後は、JREがないから。

さらに、クラウドのサーバーレス環境でアプリを実行する時、コンパイルしたアプリのサイズを小さくできる。
クラウドのリソースを抑えられるし、アプリの起動時間(ロード時間)も短くできるメリットが出てくる。
つまり、AWS lambdaみたいに、イベント駆動で多数のプロセスを並行起動するような場面では、アプリサイズが小さいほど性能も良くなるし、デプロイもやりやすい。

たぶん、クラウドの開発に特化するようにJavaも進化してきているのだろう。

クラウド上の開発がJavaに与えた影響は何なのか: プログラマの思索

【5】jdeps --jdkinternal はなぜ必要なのか?

JDKの内部APIでクラスレベルの依存関係を検索するためのコマンド。
古いJava8のJarファイルがどんなJDKライブラリを使っているか調べるために使う。
Java9以後は公開されていないJDKライブラリは排除していくべきという考え方。

Java9のモジュールへ移行する時や、jlinkコマンドで小さい専用ランタイムアプリを作りたい時に利用できるだろう。

【6】なぜServiceLoaderは必要なのか?

ServiceLoaderは、Java9以前で依存性注入(DI)を実現する方法に過ぎない。
たとえば、Sampleインターフェイスだけ公開して、SampleImpl実装クラスは呼び出さないように実装したい。
META-INF/servicesに、実装クラスを記載したテキストファイルを作って、ServiceLoaderが読み込んで動的に切り替える仕組みにしただけ。

Java9のモジュールシステムでは、ServiceLoaderを利用する場合、module-info.javaにprovides~with~でIFを書いて公開する。

「公開IFの提供」をmodule-info.javaで宣言する => provides 公開IF with 実装クラス(SPIを実装)
「公開IFの利用」をmodule-info.javaで宣言する => uses 公開IF名(SPIとして使う)

ServiceLoaderはJDBCドライバの実装にも使われているので、わりと一般的。

【7】Javaのモジュールシステムは、Javaの進化でどのような意義を持つのか?

モジュールシステムは、Javaをクラウド開発に適用したり、デプロイしたモジュールやライブラリの移植性を高めるために必要なのだろう。
しかし、Javaは過去の遺産が多すぎるために、どんどん仕様が複雑になってきていると思う。

実際、Jarの依存性のエラーメッセージの種類が多くなっているために、問題解決するのは大変だろうと想像する。

Javaのモジュールシステムは複雑性をより増している: プログラマの思索

一方、Javaはオブジェクト指向言語だけでなく、関数型言語の一面も持つ。
実際、OptionalやStreamなどのモナドのAPIはまさに関数型言語そのものだ。

Javaが関数型言語の特徴を持つようになった理由の背後には、クラウド上で多数のプロセスを並行で動かす時に、MapReduceの仕組みがあれば、負荷分散をより活用できるメリットを活かしたいからだろう。

Javaはオブジェクト指向言語ではなく関数型言語だった~「[増補改訂]関数プログラミング実践入門」はお勧めの本だ: プログラマの思索

Javaはなぜ関数型言語になろうとしているのか: プログラマの思索

JavaSilverの感想~Javaはオブジェクト指向と関数型言語の2つの性格を持つ: プログラマの思索

そんなことを考えると、20年前にUMLやJavaを使って、オブジェクト指向設計に熱狂していた時代はもう古くなっている。
そういう価値観はもはや終わったように思える。
そして、時代は更に進化しているわけだ。

一方、その進化はどんどんソフトウェアの複雑性を増やす。
ソフトウェアの本質は複雑性にあるが、その複雑性をどのように手懐けてコントロールすべきなのか?
今も昔もソフトウェア開発はこの問題から逃れられない。

| | コメント (0)

2022/10/16

クラウド上の開発がJavaに与えた影響は何なのか

クラウド上の開発がJavaに与えた影響は何なのだろうか。
考えがまとまっていないがラフなメモ。
自分はAWSも知らないし、Go言語も知らないので、間違っていたら後で直す。

【参考】
Javaがサーバレスに至るまでの道のり

元JavaエンジニアがGoに感じた「表現力の低さ」と「開発生産性」の話 - DMM inside

Ruby->Go->Scalaという習得順序がエンジニアの爆速の成長に最適である理由 - Qiita

Javaがサーバレスに至るまでの道のりの記事を読むと、いかに軽量のWebアプリケーションを実現するか、に注力しているように思える。

従来のWebアプリケーション開発では、PerlのCGIのようなプロセス指向ではなく、JavaのServletのようなマルチスレッドの方が無駄にリソースを食わず、効率が良いと歌われていた。

しかし、サーバーレス環境では、AWS Lambdaのようにアプリをキックするような仕組みなので、アプリの処理時間だけでなく、アプリのロード時間も短いほど優位だからだ。
サーバーレス環境では、アプリを常駐させて、マルチプロセスで動く。
そして、処理が終わり、無駄に多くなったプロセスはすぐに破棄される。

つまり、クラウドでは、DockerやK8sのようなコンテナのおかげで、アプリ環境を使い捨てにできるメリットがある。
アプリも、負荷分散に応じて必要なだけ常駐プロセスを増やして、不要になれば捨てればいい。

すると、アプリのビルド、デプロイの時間も短い方がいい。
さらに、「起動時間がレスポンスタイムに含まれる」ために、アプリの起動時間も短くできれば、サーバーレス環境ではレスポンスタイムをもっと短くできて、使い勝手が良くなる。

そういう発想がJavaにどんな影響を与えているのか?
クラウド上の開発がJavaに与えた影響は何なのか?

たまたま、Javaのモジュールシステムの話の中で、jlinkコマンドがなぜ必要なのか?という話があった。
JDK9以後は、JREが配布されないので、Javaファイルを実行できない。
そこでjlinkで実行ファイルを作る必要があるという話だったが、さらにもう一つ興味深い話があった。

Java Module System 入門 その1 - YouTube

Java Module System 入門 その2 - YouTube

jlinkコマンドを使うとランタイム専用アプリを作れるが、必要なJDKライブラリだけを読み込んで、Jarファイルを作成する。
すると、jlinkで作られたランタイム専用アプリのサイズは、従来のランタイム専用アプリのサイズよりもかなり小さくなる。
不要なライブラリを除いているからだ。
実際、デモ画面では200Mから30Mまで小さくできた例があった。

つまり、jlinkコマンドを使えば、クラウドのサーバーレス環境でアプリを実行する時、コンパイルしたアプリのサイズを小さくできる。
その分、クラウドのリソースを抑えられるし、アプリの起動時間(ロード時間)も短くできるメリットが出てくる。

すなわち、jlinkコマンドはクラウド上で配布するアプリに適合するように作られたものなのだろう。
Javaもクラウドに特化したアーキテクチャ設計を取り込んでいるのだろう。

そんな話を聞くと、サーバーレス環境のアプリでは、ランタイム専用アプリはバイナリファイルでサイズが小さいほど優位性が高いことになる。
マイクロサービスの実装言語はGoが主流であると聞くが、その理由は、Goで簡単にバイナリファイルが作れて、さらにそのサイズが小さいので、性能的にも優れている点があるのではないだろうか?
そして、Javaもそういう流れの設計手法を取り入れるために、jlinkコマンドのようなアーキテクチャが必要になってきたのではないだろうか?

ここで、クラウド上のシステム設計では、ドメインごとにアプリを細分化したマイクロサービスアーキテクチャが流行していて一般的になってきている。
マイクロサービスアーキテクチャが必要な理由は、普通のWebシステムでもクライアントからのアクセスがブラウザだけでなく、スマホだったりタブレットだったり、色んな端末に対応する必要があるので、公開APIを通じて制御できるよに、色々分割したほうが楽になる点があるだろう。
また、逆コンウェイ戦略のように、システム開発しやすいチーム組織の観点で設計すると、小さなコンポーネントの集まりになるようにシステムを分割して、マイクロサービスアーキテクチャで連携できるようにした方がよい点もあるだろう。

マイクロサービスアーキテクチャのそんな特徴を活かすには、おそらく、Go言語やjlinkコマンドのような実装言語やビルド方法も必要になってくるのだろう。

そんなことを考えると、クラウド上のシステム開発では、フロントWebシステムをRubyやJava、Pythonで作ったとしても、AWS lambdaやマイクロサービスの部分はGo言語やjlinkを使う、といったように、複数のプログラミング言語を適材適所に当てはめて開発する必要性があるのではないか。
つまり、クラウド上の開発は、単にコンテナ部分のアーキテクチャを使いこなすだけでなく、複数のプログラミング言語を適材適所で使い分けるノウハウも必要になってくるのだろう。

この辺りは妄想に近いかもしれないので間違っているかもしれない。
ただ、クラウドというアーキテクチャは、Webリソースをふんだんに使える富豪プログラミングを可能にしたからこそ、それに特化した設計手法がどんどん現れているのだろうと思う。

この発想が正しいのか、これから検証していく。

| | コメント (0)