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

Latest commit

 

History

History
170 lines (123 loc) · 8.85 KB

README.md

File metadata and controls

170 lines (123 loc) · 8.85 KB

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セット分の在庫が揃ったと判断するか