プログラミング

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/09/10

Javaのモジュールシステムは複雑性をより増している

Java9以降のモジュールシステムについて、探した記事のリンクをメモ。

【参考】
モジュールシステムを学ぶ - Qiita

モジュールグラフが作られる様子を学ぼう - Qiita

Java 9のモジュール機能「Project Jigsaw」の基本を紹介 (1/2):CodeZine(コードジン)

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

Java 9のリリースとこれまでのトレンドを振り返る (1/3):CodeZine(コードジン)

ソフトウェアの複雑性は本質的な性質であって偶有的なものではない: プログラマの思索

(引用開始)
立ちはだかるモジュールシステム
ビルド設定の不備に起因するエラーが増えた

Java 8 以前
「パッケージ...は存在しません」
「シンボルを見つけられません」
java.lang.ClassNotFoundException
java.lang.NoClassDefFoundError
java.lang.NoSuchMethodError

Java 9 以降(上記に加え。。。)
「モジュールが見つかりません: ...」
「パッケージ...は表示不可です」
「パッケージ...はモジュール...で宣言されていますが、エクスポートされていません」
「パッケージ...はモジュール...で宣言されていますが、モジュールfooに読み込まれていません」
「パッケージ...はモジュール...で宣言されていますが、モジュール・グラフにありません」
java.lang.module.FindException
java.lang.IllegalAccessException

(中略)
Java 8 以前のビルドエラーの原因
クラスパスに必要な jar がない
クラスパスに入れている jar のバージョンが間違っている
だいたい、クラスパスの設定を調べれば解決した(要出典)。

Java 9 以降のビルドエラーの原因
設定漏れなどにより、モジュールの解決が正常に完了できなかった
解決されたモジュールグラフが必要な形を満たしていない

モジュールグラフの作られ方を理解していないと、
エラーの原因が特定しづらい(気がする)
(引用終了)

Javaはもはやシンプルなオブジェクト指向言語ではない。
他言語に比較して不足していた機能を次々に取り込んできたために、とても複雑になっていると思う。
モジュールシステムもそうだろう。

以前なら、Mavenで必要なライブラリを管理していたが、それだけでなく、モジュールシステムを使って、どのライブラリを公開するか、必要とするか、という細かい設定ができるようになった。
便利になったように思えるが、これが曲者だ。

以前なら、ビルドパスに必要なライブラリがあるかどうかだけ気にすればよかった。
しかし、モジュールシステムの設定が正しいかどうか、確認する作業まで増えてしまった。

人月の神話」に偶有的な複雑性と本質的な複雑性の話があったが、モジュールシステムは偶有的な複雑性の範疇と思う。
昔のEJB、そしてPOJO、依存性注入などの考え方も、偶有的な複雑性の範囲と思う。

つまり、Javaは本来、コンピュータの内部構造、システムの内部構造を本来は隠蔽して、プログラマがそんな部分を気にせずに業務ロジックの実装に注力できるように整備すればいいのに、そういう部分までプログラマの能力を浪費してしまう部分がある。
とても繊細な積み木をブロックで組み立てている気持ちになる。

ソフトウェアの複雑性は本質的な性質であって偶有的なものではない: プログラマの思索

モジュールシステムについては色々調べているのでまたメモしておく。

| | コメント (0)

2022/09/02

Javaはなぜ関数型言語になろうとしているのか

今のJavaはオブジェクト指向言語である、というよりも、関数型言語になろうとしているように思えた。
ラフなメモ。

【参考】
JavaプログラマのためのIOモナド - kmizuの日記

yasuabe blog: Java8 の Optionalを Haskellの Maybeモナドっぽく使ってみる

Java の語彙で Maybe を説明してみる - ぐるぐる~

JavaでMonadをはじめからていねいに :: Igreque -> Info

モナドについてSwiftで説明してみた - Qiita

モナドとは「メソッド内の副作用の存在を戻り値の型で表現する」ためのデザインパターンです - Qiita

関数型プログラミングなんもわからん。を考えようと言うイベントを開きました。

ラムダ式や関数型インターフェイスは、まあ書けば理解できるが、OptionalクラスやStreamクラスがなぜ必要なのか、正直分かってない。
ネットで漁って、OptionalクラスはMaybeモナド、StreamクラスはListモナドである、という文章を見つけて、そんな難しい構造を持っていたのか、と今頃気づいてる。
StreamのAPIはMapReduceと同じだな、とか。

モナドを支える圏論は大学院のときに代数幾何学の一つの理論として読んだことがあるが、写像を集合とみなして公理系を打ち立てる、ぐらいのイメージしかなくて、プログラミングの観点でモナドがどのように扱われるのか、まだ理解できていない。
今は、モナドとは、メソッドを副作用を持たない関数にした時、戻り値に状態の型のように副作用の型を持たせるとみなして理解している。

普段の業務系Webシステムで、DBにデータを出し入れするぐらいであれば、関数型言語をわざわざ使って並列処理を効率化するようなシチュエーションなんてない。
ロードバランサ、Webキャッシュみたいに、大量のトランザクションを捌くような処理は、正直フレームワークの中に隠れてしまっていて、普通のプログラマであればそんな低レイヤーは気にしないし、気にしないようなオブジェクト指向なフレームワークを作るべきだと思う。

今のJavaはオブジェクト指向言語だけでなく、関数型言語のAPIも追加されているため、非常に膨大で複雑化してしまっているように思える。

なぜJavaは、わざわざ関数型言語のAPIを取りれたのか?
オブジェクト指向言語のAPIで十分なはずなのに。
Scalaの出現とも絡んでいるのか?

たとえば、Scalaの本を読むと、ScalaのAPIはJavaのAPIと非常によく似ている。
ScalaのOptionはJavaのOptionalと挙動が同じ。
ScalaのFutureはJavaのFutureと挙動が同じ。
Scalaのトレイトは、Javaのインターフェイスがdefaultやstaticで実装メソッドを持つようになったのと同じ。
もちろん、map, flatMapなどはScalaもJavaも挙動が同じ。

つまり、ScalaはJavaとかなり互換性があるように思える。
単にScalaがJVMに乗っていて、JavaAPIを呼び出せる事実以上に、言語の思想としてもJavaがScalaに影響を受けているように思える。

気になるのは、Scalaの書籍は2010年頃に大量に出版されたのに、今はあまり出版されていないこと。
人気も下火になったのかな?

「Javaはなぜ関数型言語になろうとしているのか」については調べてみる。

| | コメント (0)

2022/08/10

Javaのラムダ式の考え方

Javaのラムダ式の考え方について理解できた内容をラフなメモ。
間違っていたら後で直す。

【参考】
Javaにおけるラムダ式とメソッド参照

徹底解説! Javaのラムダ式はどういうもので、どう使えばいいのか!

javaの内部クラスおさらい - Qiita

staticな内部クラス - 技術メモ

【Java】内部クラス(インナークラス)の使い方(static class) | 侍エンジニアブログ

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

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

Java8以後は、オブジェクト指向言語ではなく関数型言語とみなした方がいいと感じた。
理由は、ラムダ式のような関数型言語の特徴を取り入れたAPIを多数取り込んでいるからだ。
マルチスレッド、Optional、Stream、IOなどの新しいAPIに触れてみると、ことごとくラムダ式を使って副作用をなくすような処理でいかに書くのか、を目指しているような気がする。

Javaのラムダ式は、RubyやPythonのクロージャみたいなものだと思う。
ただし、Javaのラムダ式では、ローカル変数は実質finalなので更新しようとするとコンパイルエラーになる。
この特徴は、Rubyのブロック、Pythonのクロージャのようなレキシカルスコープとは異なる。

Javaのラムダ式は2つの理解方法がある。

1つ目は、Javaのラムダ式は、内部クラス(Inner class)や匿名クラス(anonymous class)から生まれたものと思えばいい、
ラムダ式のイメージはこんな感じ。

依存しあったクラスが2つある。
→2つのうち1つのクラスを、内部クラスにして1つのソースファイルに統合する。
→static内部クラスにして、内部クラスのインスタンス化を廃止する。
→static内部クラスをメソッド内に取り込んで、ローカルクラスに置き換える。
→メソッド内のローカルクラスを匿名クラスに置き換える。
→匿名クラスをラムダ式に置き換えると出来上がり。

Javaの内部クラス、局所クラス、匿名クラスのような記法は現場であまり使わないから、どこで使うのだろうか、と思っていたが、ラムダ式を生み出すための前段階の仕組みと思えば自然に思える。

徹底解説! Javaのラムダ式はどういうもので、どう使えばいいのか!

2つ目は、「Javaのラムダ式は、関数型インターフェイスを実装したクラスのインスタンスを、簡単に作るための文法」。
つまり、Javaのラムダ式は関数型インフェーエイスを宣言し、実装したクラスからインスタンスを生成する仕組み。

徹底解説! Javaのラムダ式はどういうもので、どう使えばいいのか!

関数型インフェーエイスは、抽象メソッド1個だけを持つのがミソ。
FunctionalInterfaceアノテーションを付けたインターフェイスでは、自動的に関数型インフェーエイスになるが、抽象メソッドを2つ宣言したり、defaultメソッドで実装すると、関数型インフェーエイスではなくコンパイルエラーになる。

では、なぜラムダ式があると便利なのか?
マルチスレッドやStreamAPIでラムダ式を書いていると、処理の宣言と実装を1箇所に書けるのがメリットのように思う。
いちいち、別クラスを宣言して、インスタンスを生成して、メソッドを呼び出すみたいな面倒な処理をたくさん書かなくていい。

ラムダ式を使うと、Factoryクラスは不要になる。
() -> new A()のようなラムダ式を簡単に埋め込めるからだ。

ラムダ式を使うと、Strategyパターンの行数を減らせる。
ポリモルフィズムを使って、それぞれの処理を実装し、インスタンスを生成する部分をラムダ式で1行にまとめられるからだ。

「関数型インターフェイスには1つしか抽象メソッドがないので、戻り値・引数の型と順番を、関数型インターフェイスの型からJavaコンパイラが推測できます。この仕組みを型推論といいます。 」という指摘から、関数型言語の特徴である型推論をJavaでも実現できる。
Haskellと似ているのかな。

実際のエンタープライズJavaの開発現場では、ラムダ式やストリームAPIは禁止されているコーディングルールが多いかもしれない。
でも、Javaに限らず、現代の殆どのプログラミング言語に関数型言語の特徴が盛り込まれているので、今後もチェックしてみる。

残りは、モナドを理解したい。

| | コメント (0)

2022/08/06

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

Oracle Java Gold SE11 を取得するために
徹底攻略Java SE 11 Gold問題集[1Z0-816]対応 徹底攻略シリーズを解いているが、Javaはオブジェクト指向言語ではなく、Javaは関数型言語であった事実に気づき、「[増補改訂]関数プログラミング実践入門 ──簡潔で、正しいコードを書くために」を借りて読んでいる。
Oracle Java Gold SE11 では、ラムダ式とストリームAPIを習得するのがMust要件だが、実は、JavaのOptionalはHaskellのMaybeモナド、JavaのStreamはHaskellのリストモナドに相当する、という文章を読んで、インスピレーションが動いた。
こういうところまで理解しておかないと、たぶんJavaGoldは歯が立たないのだろうと思う。

この本では、Ruby、Python、Java、C++などのソースとHaskellを比較してくれているので、他言語を知っていたら理解しやすくなると思う。
関数型プログラミング言語を習得したい人にとっては、この本はMustだと思う。
まだ全部理解できていないので、これからじっくり読んで消化していく。

| | コメント (0)

2022/07/06

JavaSilverの感想~Javaはオブジェクト指向と関数型言語の2つの性格を持つ

JavaSilverに合格できたので、気づきをメモ。

【参考】
Java SE 11 Programmer I (1Z0-815-JPN) 試験 | Oracle University

Javaメモ目次(Hishidama's Java Memo)

Java新機能メモ(Hishidama's Java up-to-date)

【1】僕はJavaは好きだ。
Javaを習得した後、オブジェクト指向設計やUML、RUPを知りたくなって、開発プロセスの奥深さを学ぶことができた。
オブジェクト指向の考え方を学ぶならJavaはお勧めだと思う。

一方、Java SE11で書いてみると、Javaはオブジェクト指向言語というよりも関数型言語になろうとしている気がする。
実際、ラムダ式やvar、型推論などは関数型言語の特徴をJavaに取り入れている。
Javaに新しくストリームAPIとは何だろう?と思っていたら、何のことはない、MapReduceのAPIに過ぎない。
つまり、関数型言語の特徴を取り入れて並列処理を意識しているわけだ。

そんなことを思うと、Javaはオブジェクト指向言語と関数型言語の2つの性格を持つように進化したのだろうと思う。

以前のJavaから追加された主な文法、機能をメモしておく。
・ module-info.java (exports パッケージ, requires モジュール)
・ javaコマンドのソースファイルモードで直接実行
・ jmod, jdeps
・ アノテーション
・ ラムダ式
・ インターフェースのdefaultメソッド、privateメソッド
・ var型
・ try-with-resources 構文
・ オートボクシング
・ ジェネリクス
・ 可変長引数
・ 共変戻り値によるオーバーライド
・ ストリームAPI=MapReduce用API
・拡張for文

Javaで気づいたことをメモしておく。

【2】Javaのvarについて

varを使えば、わざわざ変数の型宣言はしなくていい。
ただし、型が判定できるような初期値でなくてはいけない。

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

しかし、varを取り入れることで、今までのJavaの文法や考え方が複雑化していると思う。
たとえば、Javaにはジェネリクスがある。
さらに、ダイアモンド演算子によって、ジェネリクスの型宣言を短く書けるようになった。

ダイアモンド演算子は右辺の型引数を省略(推論)するが、varは左辺の型を推論するから、両方を組み合わせるとややこしくなる。

変数の定義方法の変遷 Java varメモ(Hishidama's Java var Memo)

(引用開始)
ダイアモンド演算子は、右辺の型引数を省略(推論)するものである。
一方、varは左辺の型を省略(推論)するものなので、ダイアモンド演算子とは相性が悪い。
つまり「var map = new HashMap<>();」と書いたら、変数が何の型なのか分からない^^;
(実際に書くと、HashMapと推論される)
(引用終了)

【3】自動ボクシング

プリミティブ型とラッパークラスを同一視して代入できるようになった。
自動ボクシングのおかげで、プリミティブ型はオブジェクトっぽく考えられるかな。

自動ボクシング Java型メモ(Hishidama's Java type Memo)

しかし、オーバーライドする時に、int[]という配列はObject型とみなす場合がある時は正直迷った。

【4】拡張for文

RubyやPythonのようなfor文を書くことができる。
ループ処理の文法は、while→for文→拡張for文→List.forEach()へと進化しているが、過去の文法が残っているのでややこしく感じてしまう。

for each(拡張for文) Java文メモ(Hishidama's Java Statement Memo)

【5】Javaは実行時のインスタンスのメソッドが呼ばれる

Rubyと違って、Javaは実行時のインスタンスのメソッドが呼ばれる。
C++のvirtualメソッドと同じ。

つまり、Javaでは、クラスやインスタンスは、型に強く紐づく。
型は、フライパンのタイヤキの型に例えられる。

実行時のインスタンスのクラスが重要 裏Javaメモ(Hishidama's Java Memo)

この性質を使えば、ポリモルフィズムによって、実行時に動的にインスタンスを切り替えて、同一のメソッドで処理を入れ替えられる。

【6】同一クラス内の同一変数名、同一ローカル変数

(引用開始)
変数名やメソッド名に同じ名前を使える箇所がある。同じ名前が使えない箇所もある。
(引用終了)

重複する名前、同一クラス内の同一変数名、同一ローカル変数 裏Javaメモ(Hishidama's Java Memo)

メソッド内で、フィールドと引数が同じ変数名の場合、thisを使っていないとローカル変数とみなされる。
Eclipseでは、変数名が色付けされるのでフィールドなのか、ローカル変数なのか、区別できるが、エディタ上では判別できない。
普通は、潜在バグになるので、こういう紛らわしい変数名を付けないと思う。

【7】アクセス制御

Javaのアクセス制御は普通の考え方でいい。
注意点は、public/protected/privateのような修飾子がない場合は、同一パッケージ内でしか呼び出しできないこと。

アクセス制御 Javaクラス定義メモ(Hishidama's Java Class define Memo)

下記のような変な書き方もできるが、普通はしないだろう。

スーパークラスを呼ぶだけのオーバーライド 裏Javaメモ(Hishidama's Java Memo)

【8】ラムダ式は無名内部クラス(匿名クラス)を短く記述できる記法

Javaのラムダ式は、Rubyのクロージャを知っていればすぐに理解できるだろう。
一方、Javaの観点では、ラムダ式は無名の内部クラスで冗長に書き換えられる。

Javaラムダ式メモ(Hishidama's Java8 Lambda Expression Memo)

ラムダ式の注意点は、「ラムダ式内部で外側の変数を使う場合は、その変数はfinalもしくは実質的finalでなければならない。」
つまり、「ラムダ式の外側で定義されている変数を、ラムダ式内部から参照することが出来る」が、書き換えは不可。
コンパイルエラーになる。
RubyやPythonのレキシカルスコープとは異なる。

【9】リソース付きtry文

Javaでも、例外をキャッチしたらリソースClose→finallyが簡便に書けるようになった。
Rubyのbegin~rescue~ensureと似てる。

リソース付きtry文 Java文メモ(Hishidama's Java Statement Memo)

リソース付きtry文が無い頃は、finallyの中でさらに、try~catchをわざわざ書いていた。
Release It! 本番用ソフトウェア製品の設計とデプロイのためににも、それを書かなかった場合の本番障害の事例が書かれていた。

| | コメント (0)

2022/06/20

Javaのenum型はシングルトンクラスみたいだ

Javaの最新版に触れてみたら、enum型にコンストラクタやメソッドを持たせることができると分かった。
ちょっとびっくりした。

(2) よしけーさんはTwitterを使っています: 「enum にメソッド持たせるのは effective Java あたりを読まないと気がつけない気がする。。 #ドメインモデルパターンに挑戦する苦労ばなし」 / Twitter

(2) akipiiさんはTwitterを使っています: 「Effective Javaにenumのパターンが確かに書いてあった。enumでメソッドを定義する発想がなかった。後で読む」 / Twitter

あなたの知らないJava enumの使い方 - SEチャンネル

Effective Java 第3版 第6章enumとアノテーション - Qiita

Effective Java 3rd [Chapter 6] - enum とアノテーション - Little by little and bit by bit - マイペースにこつこつと

【Effective Java】項目30:int 定数の代わりに enum を使用する - The King's Museum

Effective Java 第3版 「ほぼ全章」を「読みやすい日本語」で説明してみました。 - Qiita

ドメイン駆動設計に関する勉強会で、Javaのenumにメソッドを持たせられるというツイートを見た。
Effective Java 第3版 | Joshua Bloch, 柴田 芳樹」を読んでみたら、たしかにenumに1章を割いて、詳しく説明されていた。

個人的印象としては、JavaのEnum型はシングルトンクラスみたいだ。
グローバル変数の出し入れに使える感じ。
使い道としては、業務システムのシステムコントロール定数を持たせたい時だろうか。

Effective Java 第3版 第6章enumとアノテーション - Qiitaでは、int型をEnu型で書くべき、というプラクティスは理解できる。
しかし、「拡張可能なenumをインタフェースで模倣する」プラクティスのように、通貨クラスに「+」「-」のようなメソッドを独自で実装するという発想はなかった。
こういう考え方がドメイン駆動設計の値オブジェクトの実装に役立つのだろう。

Javaから離れていたので、最新版のAPIや文法がかなり変わっているのに気づいた。
Javaはオブジェクト指向言語だというぐらいのイメージしかなかったが、RubyやPython、JavaScriptなどの良い面をJavaも取り入れようとしている印象を受ける。
Javaにラムダ式ぐらいを取り入れるのはまだよいが、ストリームAPIは何かやりすぎな感じもした。
気づいたことはまた書く。



| | コメント (0)

2022/05/26

「コーディングを支える技術」は良い本だ

コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)」を読んで、良い本と思った。
ラフなメモ書き。
プログラミング初心者の適当な感想。

【参考】
コーディングを支える技術――成り立ちから学ぶプログラミング作法:書籍案内|技術評論社

「コーディングを支える技術」著者公式ページ
「コーディングを支える技術」著者公式ページ 補足記事

【1】Javaで開発経験をした後、RubyやPythonを覚えた今、「コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)」を読むと、ああそういうことか、と気づくことがある。

【2】IF文の裏ではGOTO文が動いている、という内容を読んで、VBのことを思い出した。
VBでは、例外処理などのAPIが貧弱なので、割とGOTO文がよく出てくるので、VBは好きでなかった。
たぶん、APIが貧弱な古いプログラミング言語ほど、GOTO文が幅を利かせている。
その分、何でもできるが、とても読みづらく保守しづらい。

コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)」の一節に、大学でC言語を初めて習った人が、「while文があればfor文はいらないのでは?」と思ったらしいと書かれていた。
僕も似たような感覚を抱いていた時があった。
JavaでもRubyでも、なぜ、while文、do-while文、for文、拡張for文など、似たようなループ処理の文法があるのか?と不思議に思っていた。

理由は単純で、当初はIF文みたいにwhile文を使っていたが、カウンタ変数のスコープがブロック外にあるので使いにくいから。
だから、while文からfor文の記法により、カウンタ変数のスコープはブロック内に収まった。
さらに、拡張for文により、カウンタ変数そのものも不要になった。
さらに、ListのforEachメソッド、Rubyのブロックのように、for文はクロージャとして吸収される。
つまり、プログラミングの文法もどんどん進化しているわけだ。

【3】変数のスコープの説明では、Perlで静的・動的スコープをプログラムで説明してくれている。
Perlを初めて書いていた時、なぜlocalやmyを変数に付ける必要があるのか、分からなかった時を思い出した。

Pythonでは、わざわざglobal/nonlocalで変数のスコープを制御できるようにしてくれている。
この文法を見た時、Perlみたいだな、と思ったことを思い出した。

Rubyなら、def~endのブロックとクロージャのブロックで、変数のスコープが異なるように使い分けている。
この文法は最初は理解しづらかったが、このおかげで、Rubyはメタプログラミングでやり放題ができるわけだ。

【4】オブジェクトとクラスでは、JavaとRubyでは考え方が全く違う。
親子で継承関係にあるクラスがあった時、インスタンス変数、インスタンスメソッド、クラス変数、クラスメソッドの挙動がJavaとRubyで異なる。
Javaでは、インスタンス変数、クラス変数、インスタンスメソッド、クラスメソッドは、親子クラスそれぞれで保持する。
ただし、親クラスの変数に、子クラスのオブジェクトを代入した場合、インスタンスメソッドは、子クラスのメソッドを利用する。
つまり、Javaでは、親クラスの変数の振る舞いは、ポリモーフィズムで動的に処理が変わるように見せかけられる。

一方、Rubyでは、親子間でインスタンス変数が共通として扱われる。
親クラスが定義したインスタンス変数と同じ名前のインスタンス変数を子クラスで使うと、親クラスのインスタンス変数は子クラスで上書きされる。
この書き方に慣れると、「クラス(型)は仕様である」を時々忘れてしまう。

資格取得に40代後半からチャレンジ JavaとRubyを比較(注意しておくことその1)

資格取得に40代後半からチャレンジ JavaとRubyを比較(注意しておくことその2)

資格取得に40代後半からチャレンジ JavaとRubyを比較(注意しておくことその3)

資格取得に40代後半からチャレンジ JavaとRubyを比較(注意しておくことその4)

Javaでは「クラス(型)は仕様である」が、Rubyではメッセージを渡したオブジェクトに全てを委ねる。
Rubyはまさにダック・タイピングなので、例えばdesktop.printが変数なのか、メソッドなのかも動かしてみないと分からない。

【5】多重継承のデメリットをどう解決するか?も、Java、Ruby、Pythonなどで全く違う。
Javaは多重継承を禁止するが、インターフェイスを導入して解決しようとした。
Javaでは、継承の代わりに委譲を進めるが、さらに委譲の参照をソースにハードコードするのではなく、依存性の注入により、設定ファイルを使って動的に変更できるような手法が多くなった。
JavaのフレームワークではDIが多いから、設定ファイルにXMLを用いることが多く、その分、プログラムを書くよりもXMLを書いている時が多くてあまり楽しくなくなる。

Rubyは、多重継承の問題を「どの順番で継承関係を検索するか」という問題に置き換えた。
Rubyでは継承関係は、Objectを頂点とするツリー構造だから、継承ツリーをメソッド検索することで解決しようとする。
include, prepend, refinementsはそういう使い道だろう。
また、Rubyでは、どうしても多重継承したい時は、モジュールでMix-inを使う。

Pythonでは多重継承できる、と聞いていてまだ理解できていなかったが、「コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)」によれば、C3線形化のアルゴリズムで継承関係の検索を決めることで解決しているわけだ。

[Python入門]多重継承:Python入門(1/2 ページ) - @IT

Pythonのメソッド解決順序(MRO) - 情報系大学院生の勉強メモ

【6】それから、並行処理のプログラミングの章も非常に分かりやすい。
Windows3.1やMacOS9は強調マルチタスクで、プリエンプティブマルチタスクではなかった、という一節を読んだ時、昔のPowerbookがMacOS9だった時を思い出した。

並行処理の問題点は、競合状態が起きること。
競合状態は3つある。
1つ目は、2つの処理が変数を共有している。
2つ目は、少なくとも1つの処理がその変数を上書きする。
3つ目は、一方の処理が一段落付く前に、もう一方の処理が割り込む可能性がある。

1つ目の対策は、結局スレッドを使っているので、変数を共有しない方向で解決していない。
アクターモデルのように、メッセージを送ることで解決しようとしている。
Erlangがその例だろう。

2つ目の対策は、メモリを共有しても書き換えないようにする方針もある。
全ての変数はImmutableにすればいい。
Haskellなどの関数型言語がそうだろう。

3つ目の対策は、協調的なスレッドを使う。
たとえば、Rubyのファイバー、Cのコルーチン、Pythonのジェネレータ。

あるいは、ロック、ミューテックス、セマフォ、モニタのような機構を使う。
Javaなら、synchronizedブロックで囲むだけでいい。

【7】自分は「プログラミングのできない山羊」出身なので、プログラミングを経験してやっとこういう本の内容が分かってきたという気がしている。
ソフトウェア開発は、数学や物理、経済学、財務会計、心理学などと違って、やっぱり独特の考え方がいると思う。
自分の中で言語化できていない部分は気づいたらブログで残しておく。

「プログラミングのできる羊とできない山羊を区別する方法」の記事のリンク: プログラマの思索

「60%の人間はプログラミングの素質がない」記事のリンク: プログラマの思索

プログラミングしてる時はでっかいピタゴラ装置を作ってるみたいな感じ: プログラマの思索

プログラミングは「ブロックを組み合わせる」感覚に似ている: プログラマの思索

| | コメント (0)

2022/01/09

「RubyやRailsは終わった」という記事のリンク

「RubyやRailsは終わった」という記事があったが本当なのか?
見つけた記事だけをリンクしておく。
自分が後で読むためのメモ書き。

【参考】
“Rubyは死んだ”のか? まつもとゆきひろ氏が語る「プログラミング言語サバイバル」とRubyの未来 - Part1 - ログミーTech

Ruby2系はチームの幸福度を最大化できなかった - Qiita

「Railsは終わった」と言われる理由 - Qiita

Rubyは死んだというが。流行り廃りに拘らず、便利なものは活用すればいいのに - Qiita

Rubyは果たして死んだのか | 日経クロステック(xTECH)

Rubyは終わった?将来性と今後の展望をまとめてみた│エンジニアハック

将来性のないプログラミング言語5選として「Ruby」が挙がり話題に | スラド IT

個人的にはRubyは好きだ。
メタプログラミングRuby 第2版」を読んで、色々動かして、やっとダックタイピングの意味が分かった。
やはりRubyはJavaとは違う。
Rubyは究極のオブジェクト指向言語なのかもしれない。

また、Rubyというコミュニティも素晴らしいと思う。

なぜソフトウエア後進国の日本でRubyは成功したのか?~ソフトウェアの成功の秘訣はコミュニティ、そしてコンウェイの法則にある: プログラマの思索

一方、RubyはPerlのシンタックスを受け継ぐ部分があるせいか、初心者には読みにくい記法がある。
慣れないと使いこなせない部分もある。

「Rubyのしくみ」を読んだ後のRubyの感想: プログラマの思索

Ruby技術者認定試験の感想: プログラマの思索

Ruby初心者が間違いそうなこと: プログラマの思索

メタプログラミングRubyの感想: プログラマの思索

Rubyのブロック、Proc、lambdaのメモ: プログラマの思索

RedmineやRubyについては今後も追いかけていく。


| | コメント (0)

プログラミングしてる時はでっかいピタゴラ装置を作ってるみたいな感じ

「プログラミングしてる時はでっかいピタゴラ装置を作ってるみたいな感じ」というツイートを見つけたすごく共感した。

【参考】
Rui UeyamaさんはTwitterを使っています 「少数派の意見かもしれないけど、プログラミングしてるときはでっかいピタゴラ装置を作ってるみたいな感じで、数学が関係ある感じがあんまりしない。」 / Twitter

結城浩さんはTwitterを使っています 「@rui314 デバッグしてるときと証明読んでるときはなんか似てる。素直に読みつつ穴探ししてるみたいな感覚。」 / Twitter

英語勉強中さんはTwitterを使っています 「@rui314 めちゃくちゃわかります。僕は書いてるとき数学のことなんか考えてないです」 / Twitter

akipiiさんはTwitterを使っています 「プログラミングはこの感覚に近いな」 / Twitter

プログラミングは「ブロックを組み合わせる」感覚に似ている: プログラマの思索

「プログラミングのできる羊とできない山羊を区別する方法」の記事のリンク: プログラマの思索

「60%の人間はプログラミングの素質がない」記事のリンク: プログラマの思索

プログラマに必要なスキル: プログラマの思索

プログラムを書いている時、数学の知識を使っているかと言われるとそうではない気がする。
むしろ、APIやFWのライブラリをまずは頭に叩き込んでおき、自分が実装したい目標に対して、それらAPIをどうやって組み合わせて意図通りに動かすか、に注力している。
ちょうど、ブロックで巨大な積み木を組み立てている感じに似ている。

だから、「プログラミングしてるときはでっかいピタゴラ装置を作ってるみたいな感じ」にはとても共感するし賛同する。

たとえば、RDBでSQLをデータ抽出したり、機械学習や深層学習のライブラリを使って母集団を推定したり、マーケットを予測したり、Web上の通信を暗号化したりする時、数学の理論はAPIやライブラリの中に隠れてしまっている。
それらライブラリを呼び出すだけで、高尚な理論を使えるのは素敵だが、だからと言って、プログラミングが楽になっているわけではないと思う。

一方、やりたいことを実現するには、PythonやRDB、Webサーバー、Dockerなどの開発環境を揃えて、Githubで構成管理し、CIツールでビルド&デプロイできるようにして、Jupyter を動かせるようにしたり、IntelliJなどの開発環境を構築したり、とプログラミングの前準備がすごく多い。
普通の初心者はこの部分で挫折するのだろうと思う。

僕自身、新しい環境を揃えてプログラミングスタイルを覚える時は割と苦痛に感じる時もあった。
Ruby on Railsもそうだし、AWSでの環境構築、GNS3でのCiscoルータ&スイッチの環境構築の時もそうだ。
Python+Anacondaはまだマシだった。
プログラミングは奥が深い。

| | コメント (0)

より以前の記事一覧