Skip to content
This repository has been archived by the owner on Nov 2, 2023. It is now read-only.

lambig/java-training

Repository files navigation

Java課題は本リポジトリで行う。
本リポジトリはメンテナンス中以外はアーカイブされているため、読み取り専用として扱うこと。

0. 準備

Docker上で開発できるようになっているので、
ローカルにJDKを導入する必要もなく、好きにやってもらえればOK。

以下はVSCodeでの準備例を示す。
Extension Pack for javaの利用を推奨。(そのうち設定ファイルに追加予定)

  1. コンテナ外での操作
    1. リポジトリをクローンする。
    2. リポジトリルートに移動してコンソールを開き、docker-compose up でjava環境を起動する。
    3. VSCodeのremote explorerで↑で起動したコンテナに接続する
      • ディレクトリはusr/project/appディレクトリを選択する
  2. コンテナ内での操作(appディレクトリ)
    1. コンソールからgradle buildコマンドを実行する (npm iや composer installのようなつもりで)
    2. プロダクトコードはsrc/main/java以下、テストコードはsrc/test/java以下に配置される。
    3. gradle run testコマンドを実行する (これでテストが実行できる)

1. bookパッケージ: 本をしまうなら……

初期状態ではbookパッケージのBookAppで処理を行っている。
改善に当たってはプロダクト/テスト双方とも、bookパッケージ内部全体を改修してよいので
こころおきなく手腕を振るわれたい。
ただし、テストの既存ケースはそのまま残すこと。

シチュエーション1 重複なく本を確保したい

BookApp#シチュエーション1_本のリストに重複なく本を足したい メソッドがテーマ
このメソッド自体は一応目的を達成しているが、
不満点が山とある状態からスタート

  • 複雑度が高すぎるし見通しが悪すぎる
  • BookApp以降、本リストに重複がないことの保証は都度行う必要がある
  • 状態依存が大きく、何か他の条件等を追加するときの改修が非常にやりにくい

2. dateパッケージ: 「日付」

シチュエーション1 日付、と、日付……?

あるサービスにおいて、日付文字列を取得するためのAPIが一つ定義されている。
※ここでのAPIはコントローラを模したアプリケーションクラス DateApi とする。

  • 現時点での実日時を返却するAPI
    • ISO8601拡張形式(秒まで)の文字列で返却する
      • 例: 1985-10-26T01:35:00

ここに、次の新しいAPIを追加したい。

  • 現在の営業日付、および現在営業中であるかを返却するAPI
    • 例: 1985年10月25日 / false

APIの定義はスタブとして用意されたものを利用して、実装を付与してみよう。

ゴール

  1. 業務知識をクラスに落とし込むにあたって、概念図を作成する
  2. 概念図に基づきメソッドを実装し、UTでその動作を保証する
    1. スタブメソッドを実装する
    2. スタブメソッドのUTを実装する
    3. 既存メソッドのUTを実装する
    4. 既存メソッドをリファクタする

仕様に関して

  • 日付の扱いについてはOffsetDateTimeとAsia/Tokyoのタイムゾーンを利用する。(改修前実装参照)
  • このサービスの「実日時」では、日付の秒数を「切り捨て」て扱うことになっている。
    • 実日時と言いつつ生の日時データでない点に注意
  • 「営業日時」について
    • 実日時と異なり、秒数は「繰り上げ」て処理することになっている。
    • 実日時と異なり、午前4時を境に日付が変わり、それまでは前日の扱いとなる。
      • (どうやら午前0時から実行される日次集計バッチの終了を待っているらしい……)
    • このサービスにおいて、営業時間は 09:00 ~ 18:00 としている。
  • このAPIにはまともなUTがない。このタイミングで実装するべきだろう。
    • まずはテスト可能性を付与する必要がありそうだ。

ヒント

  • どうやらOffsetDateTimeをそのまま引っ張りまわすのは筋が悪そう
  • 営業日付を出力するにせよ、内部では日時で持った方が良さそう
  • Composition over inheritance
  • 実日時と営業日時には、できればメソッドに互換性を持たせたい

日時処理の例

OffsetDateTime 実日時 営業日付 メモ
1985-10-25T23:59:59 1985-10-25T23:59:00 1985年10月25日 実日時の秒切捨て
1985-10-26T03:59:00 1985-10-26T03:59:00 1985年10月25日 営業日付が遅れている
1985-10-26T03:59:01 1985-10-26T03:59:00 1985年10月26日 秒切上げで営業日付の遅れが消えた
1985-10-26T04:00:00 1985-10-26T04:00:00 1985年10月26日 営業日付が実日付と一致する時刻

3. brokerパッケージ: pub/subブローカーを作ろう

シチュエーション1 Runnableを登録したい

複数のイベントを登録可能で、かつそれぞれのイベントに複数のコールバックを登録可能な
RunnerBrokerインターフェースを定義しておいたので、
実装クラスを作成の上、作成済みのRunnerBrokerTestで検証しよう。

シチュエーション2 Consumerを登録したい

Runnableではなく、Consumerを登録できるケース。
インターフェースやテストの定義はないので自分でなんとかしよう。

シチュエーション3 待ちの発生するConsumerを効率よく実行したい

2の実装の時点で、イベントの連鎖が可能になっている。
そこで、処理に待ちが発生してしまうConsumerばかりが登録される前提で、
全体的な待ち時間の無駄をできるだけ押さえられるようなBrokerを実装しよう。
なお、待ちが発生するConsumerはThread#sleepでシミュレートしてよい。

シチュエーションChallenge Functionを登録したい

通常、イベントを連鎖させるにはConsumer内でブローカーに対して
subscribeなりpublishなりを行う必要があるが、 Functionの戻り値をブローカーに与えるような書き方はできないだろうか?

  • ブローカー側のインターフェースを工夫することになるだろう。
  • もちろん、待ちが発生することを想定したい。
  • 後段のイベントが続く前提のFunctionばかりではないことに注意しよう。
    • 単独で終了するイベントのコールバックはConsumerで受け取っても良いかもしれない。

4. testablityパッケージ: おてがるDI

シチュエーション1 踏んだり蹴ったり

このモジュールは実装が間違っているうえにテストが書けない。
(特殊なモックライブラリを使わない限り。)

  1. テストできるように改修し
  2. 間違いを見つけて修正しよう

ヒント

  • 参照透過性の有無で処理を分けるとよい。
  • テストケースは細かく分けて3~4メソッド実装するとよい。

5.picklesパッケージ: RxJavaを試そう

brokerパッケージで練習した処理で待ち時間の無駄を押さえる方法は練習した。
が、アプリケーションの規模が大きくなってくると自力での管理も大変になるし、
実装に機械の都合が含まれるようになってくるのも嬉しくない。
そこでそういうことが得意なReactiveXのJava実装の一つ、RxJavaライブラリを利用する。
(ちなみに特別な事情がなければプロダクトではProject Reactorを利用したほうがよい。
通常Javaでリアクティブプログラミングを行う際はSpring WebFluxが簡単だが、
これはProject Reactorを前提にするため。)

シチュエーション1 現状の工程を理解しよう

簡略化された梅干し製造工程をRxJavaを利用して表現してある。
まずは処理の流れを追い、何が起きているかを理解しよう。
工程の所要時間などをいじってみるのもよい。

シチュエーション2 箱詰めを作ろう

梅製品の箱詰めを同様に作ろう。
箱詰めのモデルや工程は実装済みなので、以下を考えてうまく箱詰めを出力する仕組みを考えよう。

  1. どのように各工程に梅を分配するか
  2. どうやって1セット分の在庫が揃ったと判断するか

Releases

No releases published

Packages

No packages published