2016年にJava8+JavaFXで作ったGUIツールをリファクタリングした
内容
- 2020/5/4から4年前、当時学生のころに趣味で作ったGUIツールのリファクタリングの話を書く
- リファクタリングしたリポジトリはこちら
背景
-
2016年の7月にRPGツクールの非公式フォーラムにTKoolFacetileMaker2というツールを公開した
- https://github.com/jiro4989/TKoolFacetileMaker2
- これを作成した当時はJava8がまだまだ新しかった時代だった記憶
- 4年も経った今はJavaも14になった
- 仕事でJavaを書かなくなり、NimやGoへの興味関心が強くなった
- 結果メンテを事実上放置していた
- 4年経ってなお使っている人がちらほらいるのがメール通知から知っている
- メンテナンスをする義務はないが、技術者として古くなったものをそのまま残しておきたくない気持ちが強くなってきた
- ユーザにJava8をインストールしてもらうことが申し分けなくなってきた
- 僕がこのツールを使うことはほぼないだろうけれど、重い腰をあげてメンテナンスすることにした
抱えていた問題
- このツールを作った当時はまだ学生だったこともあり、色々作りに問題があった
- 例えば
- antという古いビルドツールでビルドしている
- パッケージやクラス命名規則が定まっておらず、Javaプロジェクトで一般的でない
- パッケージ名だとドメインの逆順がJavaでは一般的
- そうなってなくて奇妙
- MVCスタイルに則っておらず、ロジックが至るところに散らかっている
- コントローラークラスにロジックが固まっていたりそうでなかったりする
- 控えめに言って、クソ
- プロジェクト構造がJavaプロジェクトの一般的な構造でなく、IntelliJに取り込めない
-
src/main/java
とかはない
-
resources
がソースコードと一緒に存在する
- テストコードがない
-
依存ライブラリの一部のコードが残っておらず、そもそもビルドできない
- 自作のオレオレライブラリに依存しているがオレオレライブラリのソースコードが残っていない
- ビルド手順が残っておらずビルド方法がわからない
- 依存ライブラリが行方不明で取得方法もわからずビルドの仕方がわからない
- 2018年にも一度リファクタリングしようとして挫折した
- gradleに移しつつkotlinに移植しようとして難しくてダレた
- Oracle Java8からOpenJDK11に上げるとJavaFXがJDKから分離されたことでビルド方法が変わる
- 色々あって挫折
- 更に2年後、色々検討した結果、リファクタリングしてJavaのままメンテナンスを続ける方針に固まった
問題の分解
- 前述の問題を分解して、一つ一つ潰していく方針にした
-
GitHubのIssuesに問題、やることを明確にした
- いっぺんにやろうとすると死ぬ
- 後でやるから今はやらないという割り切り
- issues
- gradleでビルドできるようにする
- 最初は手動ビルドでいい
- CIでビルドできるようにする
- いつでもビルド/リリースできるようにする
- コードフォーマットする
- Googleコーディングスタイルに則る
- Javaをアップグレードする
- Java8からJava14へ
- リファクタリングする
- テストコードを書く前提にする
- MVCにする
- CIで自動テストする
- テストコード整備する
- ドキュメント整備する
- 開発者向けのドキュメント(自分用)のを書く
大事にしたこと
- とにかく「ビルドできる状態を維持する」が最優先
- CIで自動ビルドできる状態を整えてからがスタート
- 壊れたことを早期発見できないと死ぬ
- リリースビルドをローカルPCで行なわない
- 開発環境は変わりうる
- CI環境でビルドしてリリースできれば、いつでもリリースできる
- 何なら他人に引き継ぐことも可能
- CIで自動テストする前提で実装する
- UIとロジックを分離する
- ロジック部分だけテストできるようにする
- ロジックのモデル化
やったこと/できるようになったこと
-
2016年の7月にRPGツクールの非公式フォーラムにTKoolFacetileMaker2というツールを公開した
- https://github.com/jiro4989/TKoolFacetileMaker2
- これを作成した当時はJava8がまだまだ新しかった時代だった記憶
- 4年も経った今はJavaも14になった
- 仕事でJavaを書かなくなり、NimやGoへの興味関心が強くなった
- 結果メンテを事実上放置していた
- 4年経ってなお使っている人がちらほらいるのがメール通知から知っている
- メンテナンスをする義務はないが、技術者として古くなったものをそのまま残しておきたくない気持ちが強くなってきた
- ユーザにJava8をインストールしてもらうことが申し分けなくなってきた
- 僕がこのツールを使うことはほぼないだろうけれど、重い腰をあげてメンテナンスすることにした
抱えていた問題
- このツールを作った当時はまだ学生だったこともあり、色々作りに問題があった
- 例えば
- antという古いビルドツールでビルドしている
- パッケージやクラス命名規則が定まっておらず、Javaプロジェクトで一般的でない
- パッケージ名だとドメインの逆順がJavaでは一般的
- そうなってなくて奇妙
- MVCスタイルに則っておらず、ロジックが至るところに散らかっている
- コントローラークラスにロジックが固まっていたりそうでなかったりする
- 控えめに言って、クソ
- プロジェクト構造がJavaプロジェクトの一般的な構造でなく、IntelliJに取り込めない
-
src/main/java
とかはない
-
resources
がソースコードと一緒に存在する
- テストコードがない
-
依存ライブラリの一部のコードが残っておらず、そもそもビルドできない
- 自作のオレオレライブラリに依存しているがオレオレライブラリのソースコードが残っていない
- ビルド手順が残っておらずビルド方法がわからない
- 依存ライブラリが行方不明で取得方法もわからずビルドの仕方がわからない
- 2018年にも一度リファクタリングしようとして挫折した
- gradleに移しつつkotlinに移植しようとして難しくてダレた
- Oracle Java8からOpenJDK11に上げるとJavaFXがJDKから分離されたことでビルド方法が変わる
- 色々あって挫折
- 更に2年後、色々検討した結果、リファクタリングしてJavaのままメンテナンスを続ける方針に固まった
問題の分解
- 前述の問題を分解して、一つ一つ潰していく方針にした
-
GitHubのIssuesに問題、やることを明確にした
- いっぺんにやろうとすると死ぬ
- 後でやるから今はやらないという割り切り
- issues
- gradleでビルドできるようにする
- 最初は手動ビルドでいい
- CIでビルドできるようにする
- いつでもビルド/リリースできるようにする
- コードフォーマットする
- Googleコーディングスタイルに則る
- Javaをアップグレードする
- Java8からJava14へ
- リファクタリングする
- テストコードを書く前提にする
- MVCにする
- CIで自動テストする
- テストコード整備する
- ドキュメント整備する
- 開発者向けのドキュメント(自分用)のを書く
大事にしたこと
- とにかく「ビルドできる状態を維持する」が最優先
- CIで自動ビルドできる状態を整えてからがスタート
- 壊れたことを早期発見できないと死ぬ
- リリースビルドをローカルPCで行なわない
- 開発環境は変わりうる
- CI環境でビルドしてリリースできれば、いつでもリリースできる
- 何なら他人に引き継ぐことも可能
- CIで自動テストする前提で実装する
- UIとロジックを分離する
- ロジック部分だけテストできるようにする
- ロジックのモデル化
やったこと/できるようになったこと
- antという古いビルドツールでビルドしている
- パッケージやクラス命名規則が定まっておらず、Javaプロジェクトで一般的でない
- パッケージ名だとドメインの逆順がJavaでは一般的
- そうなってなくて奇妙
- MVCスタイルに則っておらず、ロジックが至るところに散らかっている
- コントローラークラスにロジックが固まっていたりそうでなかったりする
- 控えめに言って、クソ
- プロジェクト構造がJavaプロジェクトの一般的な構造でなく、IntelliJに取り込めない
-
src/main/java
とかはない -
resources
がソースコードと一緒に存在する
-
- テストコードがない
-
依存ライブラリの一部のコードが残っておらず、そもそもビルドできない
- 自作のオレオレライブラリに依存しているがオレオレライブラリのソースコードが残っていない
- ビルド手順が残っておらずビルド方法がわからない
- 依存ライブラリが行方不明で取得方法もわからずビルドの仕方がわからない
- gradleに移しつつkotlinに移植しようとして難しくてダレた
- Oracle Java8からOpenJDK11に上げるとJavaFXがJDKから分離されたことでビルド方法が変わる
- 色々あって挫折
- 前述の問題を分解して、一つ一つ潰していく方針にした
- GitHubのIssuesに問題、やることを明確にした
- いっぺんにやろうとすると死ぬ
- 後でやるから今はやらないという割り切り
- issues
- gradleでビルドできるようにする
- 最初は手動ビルドでいい
- CIでビルドできるようにする
- いつでもビルド/リリースできるようにする
- コードフォーマットする
- Googleコーディングスタイルに則る
- Javaをアップグレードする
- Java8からJava14へ
- リファクタリングする
- テストコードを書く前提にする
- MVCにする
- CIで自動テストする
- テストコード整備する
- ドキュメント整備する
- 開発者向けのドキュメント(自分用)のを書く
- gradleでビルドできるようにする
大事にしたこと
- とにかく「ビルドできる状態を維持する」が最優先
- CIで自動ビルドできる状態を整えてからがスタート
- 壊れたことを早期発見できないと死ぬ
- リリースビルドをローカルPCで行なわない
- 開発環境は変わりうる
- CI環境でビルドしてリリースできれば、いつでもリリースできる
- 何なら他人に引き継ぐことも可能
- CIで自動テストする前提で実装する
- UIとロジックを分離する
- ロジック部分だけテストできるようにする
- ロジックのモデル化
やったこと/できるようになったこと
カスタムJREで動作可能になった
- カスタムJREと一緒に配布しているので、ユーザPCに事前にJREをインストールしなくてよくなった
- 同封の起動スクリプトをダブルクリックでGUIが起動する
- Javaをインストールしていない環境で起動したときは感動した...
- ファイルサイズは50MBほどになった
- 結構大きいけれどやむなし
CIで自動ビルド、自動リリース
- GitHubActionsでリリースドラフトを公開するとバイナリがリリースされる
- https://github.com/jiro4989/TKoolFacetileMaker2/releases
自動フォーマット
google-java-format-gradle-pluginでビルド前にフォーマットするようにした
テストカバレッジの可視化
- JacocoというGradleプラグインでテストカバレッジを取得できるようにした
- CodeCovでテストカバレッジを可視化した
- Modelなどのロジック部分のテスト網羅率を90%以上にした
- https://codecov.io/gh/jiro4989/TKoolFacetileMaker2
- UI部分のテストは難しそうだったので諦めた
コードのダイエット
- 不要と判断した機能はばっさり削除したのでコード行数が大分減った
- JavaFXに元々備わっているプロパティバインドという機能をフル活用した
- これで値をセットしてまわらなくてよくなった
リファクタリング前
- テストコードはなかった
$ find src/ -name '*.java' | xargs wc -l
31 src/application/fileList/FileListHBox.java
189 src/application/fileList/FileListHBoxController.java
31 src/application/imageViewer/ImageViewerBorderPane.java
288 src/application/imageViewer/ImageViewerBorderPaneController.java
92 src/application/Main.java
471 src/application/MainController.java
44 src/application/options/Numberings.java
104 src/application/options/Options.java
40 src/application/options/OptionsStage.java
165 src/application/options/OptionsStageController.java
69 src/application/options/Separators.java
36 src/application/outputViewer/MyButton.java
122 src/application/outputViewer/MyImageView.java
33 src/application/outputViewer/OutputViewerAnchorPane.java
130 src/application/outputViewer/OutputViewerAnchorPaneController.java
25 src/application/TKoolVersion.java
29 src/application/version/VersionStage.java
42 src/application/version/VersionStageController.java
1941 total
完了後
- テストケース数は90になった
# main
$ find src/main/java/ -name '*.java' | xargs wc -l
8 src/main/java/com/jiro4989/tkfm/data/CropSize.java
42 src/main/java/com/jiro4989/tkfm/data/Position.java
38 src/main/java/com/jiro4989/tkfm/data/Rectangle.java
67 src/main/java/com/jiro4989/tkfm/Main.java
422 src/main/java/com/jiro4989/tkfm/MainController.java
235 src/main/java/com/jiro4989/tkfm/model/CroppingImageModel.java
21 src/main/java/com/jiro4989/tkfm/model/ImageFileModel.java
52 src/main/java/com/jiro4989/tkfm/model/ImageFilesModel.java
211 src/main/java/com/jiro4989/tkfm/model/PropertiesModel.java
133 src/main/java/com/jiro4989/tkfm/model/TileImageModel.java
15 src/main/java/com/jiro4989/tkfm/util/ImageUtil.java
6 src/main/java/com/jiro4989/tkfm/Version.java
1250 total
# test
$ find src/test -name '*.java' | xargs wc -l
13 src/test/java/com/jiro4989/tkfm/data/CropSizeTest.java
29 src/test/java/com/jiro4989/tkfm/data/PositionTest.java
26 src/test/java/com/jiro4989/tkfm/data/RectangleTest.java
212 src/test/java/com/jiro4989/tkfm/model/CroppingImageModelTest.java
28 src/test/java/com/jiro4989/tkfm/model/ImageFileModelTest.java
64 src/test/java/com/jiro4989/tkfm/model/ImageFilesModelTest.java
143 src/test/java/com/jiro4989/tkfm/model/PropertiesModelTest.java
120 src/test/java/com/jiro4989/tkfm/model/TileImageModelTest.java
43 src/test/java/com/jiro4989/tkfm/util/ImageUtilTest.java
13 src/test/java/com/jiro4989/tkfm/VersionTest.java
691 total
テストコードの整備
- 前述の通りロジック部分のテストカバレッジを90%以上にした
- JavaFXのテストをするためにTestFXプラグインを導入した
- CI環境だとDISPLAYがないのでテストがコケる
-
xvfb-run
というコマンドで回避
-
- CI環境だとDISPLAYがないのでテストがコケる
- JUnit5を使った
- Goだとテーブルドリブンテストを書くので同様のテストを書いた
- Junit5のParameterizedTest便利
かかった時間
- 設計の時間も含めて66時間かかった
- たかだか2000行のプログラムに66時間もかかると思ってなかった...
- GWが全部消えた+GW終わった後もやってた
- たかだか2000行のプログラムに66時間もかかると思ってなかった...
- GWが全部消えた+GW終わった後もやってた
苦戦したこと
自分が作ったツールに自分で苦しんでるとかバカじゃねえの(嘲笑)
gradle
- そもそもgradleを書いたことがなかった
- 書き方や概念を理解する必要があった
- mavenは書いたことあるのでmavenでも良かったが、スクリプト言語のが好みなのでgradleにした
JavaFX分離問題 (jmods)
- Oracle Java8のころはJDK内にJavaFXが含まれていた
- Java9以降でJavaで無料で開発するにはOpenJDKを使う必要がある
- OpenJDKも同様にJavaFXはJDK内に含まれていない
- JavaFX SDKとJavaFX JMODSを使用してモジュール機構を使う必要がある
- Java8で書いていた当時には存在しなかったモジュール機能の理解に苦戦
カスタムJRE
- PCにJREをインストールして動かすスタイルはもう古い
- アプリケーションごとにJREを内包するスタイルが今後は一般的になる
- 動作するのに最低限必要なランタイムのみ含むカスタムJREを作成し、アプリケーションと一緒に配布する
- jlinkというツールを使う
- 前述のモジュールと合わせて学習する必要があった
オレオレライブラリのソースコードがない
- 自分で配布していた実行可能JARからクラスファイルを抜いてきてjadでデコンパイルして生成されたコードから復元した
- 自分の配布物をデコンパイルする日がくるとは...
感想
やってよかった
- 機能的には増えたどころかむしろ減った
- けれど保守性は上がった
- 自動テストできるようになった
- いつでもリリースできるようになった
- Java8を使い続けないといけない呪縛から開放された
- Java14にできて良かった
4年前の反省
- 使わない機能は実装しない
- 保守コストが増加するだけで、良いことなし
- UIとロジックの密結合は避ける
- 画面を手で操作しないとテストできない状態は良くない
- テスト自動化を阻害する
- テストコードを書こう
- 正しい挙動がコードに残る
- 考慮していなかった振舞いに気付ける
- テストを書く前提の実装になる
- 品質が向上する
- テストカバレッジをとろう
- CodeCovで可視化したことでテストしていない分岐を発見できた
- 自動テスト環境を作ろう
- 今回はまっさきに自動ビルド/テスト環境を[GitHubActions]で構築したが正解だった
- 挙動が変わったことにすぐに気づける
- 自動テスト環境は早く構築すればするほど効果でる
- 自動リリース環境を作ろう
- ビルド/リリース方法は忘れる
- リリースできる環境は変わる
- ホストPCはOSレベルで変わる
- ホストPCだけでビルドしてると「気づかない依存」をしているかもしれない
- 今回だと「ソースコードの残っていないオレオレライブラリ」がまさにそれ
- 当時はビルドできたけれど今は出来ない
- CI環境でリリースできるようにしていれば、依存がかならず何処かにコード化される
- 誰でも何時でもビルド/リリースできるようにCI環境上に構築しておくべき
- 4年前の僕の実装はクソだった
- ごみ
- ばか
- うんこ
今後
- このツール以外にもJavaFXで作ったツールがいくつかあるので、同様のリファクタリングをしていきたい
- (ユーザほとんどいないので、ただの自己満足だけど)
まとめ
- 最初からちゃんと設計しよう
- テストはちゃんと書こう
- 自動化しよう
- デファクトスタンダードに従おう
- けれど保守性は上がった
- Java14にできて良かった
- 保守コストが増加するだけで、良いことなし
- 画面を手で操作しないとテストできない状態は良くない
- テスト自動化を阻害する
- 正しい挙動がコードに残る
- 考慮していなかった振舞いに気付ける
- テストを書く前提の実装になる
- 品質が向上する
- CodeCovで可視化したことでテストしていない分岐を発見できた
- 今回はまっさきに自動ビルド/テスト環境を[GitHubActions]で構築したが正解だった
- 挙動が変わったことにすぐに気づける
- 自動テスト環境は早く構築すればするほど効果でる
- ビルド/リリース方法は忘れる
- リリースできる環境は変わる
- ホストPCはOSレベルで変わる
- ホストPCだけでビルドしてると「気づかない依存」をしているかもしれない
- 今回だと「ソースコードの残っていないオレオレライブラリ」がまさにそれ
- 当時はビルドできたけれど今は出来ない
- CI環境でリリースできるようにしていれば、依存がかならず何処かにコード化される
- 誰でも何時でもビルド/リリースできるようにCI環境上に構築しておくべき
- 今回だと「ソースコードの残っていないオレオレライブラリ」がまさにそれ
- ごみ
- ばか
- うんこ
- このツール以外にもJavaFXで作ったツールがいくつかあるので、同様のリファクタリングをしていきたい
- (ユーザほとんどいないので、ただの自己満足だけど)
まとめ
- 最初からちゃんと設計しよう
- テストはちゃんと書こう
- 自動化しよう
- デファクトスタンダードに従おう
最初からこれらをやっていれば、こんな辛い思いはしなくてすんだのに...
以上
Author And Source
この問題について(2016年にJava8+JavaFXで作ったGUIツールをリファクタリングした), 我々は、より多くの情報をここで見つけました https://qiita.com/jiro4989/items/2bc2cbc4ebf695ed7a2c著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .