Java課題は本リポジトリで行う。
本リポジトリはメンテナンス中以外はアーカイブされているため、読み取り専用として扱うこと。
Docker上で開発できるようになっているので、
ローカルにJDKを導入する必要もなく、好きにやってもらえればOK。
以下はVSCodeでの準備例を示す。
Extension Pack for javaの利用を推奨。(そのうち設定ファイルに追加予定)
- コンテナ外での操作
- リポジトリをクローンする。
- リポジトリルートに移動してコンソールを開き、docker-compose up でjava環境を起動する。
- VSCodeのremote explorerで↑で起動したコンテナに接続する
- ディレクトリはusr/project/appディレクトリを選択する
- コンテナ内での操作(appディレクトリ)
- コンソールからgradle buildコマンドを実行する (npm iや composer installのようなつもりで)
- プロダクトコードはsrc/main/java以下、テストコードはsrc/test/java以下に配置される。
- gradle run testコマンドを実行する (これでテストが実行できる)
初期状態ではbookパッケージのBookAppで処理を行っている。
改善に当たってはプロダクト/テスト双方とも、bookパッケージ内部全体を改修してよいので
こころおきなく手腕を振るわれたい。
ただし、テストの既存ケースはそのまま残すこと。
BookApp#シチュエーション1_本のリストに重複なく本を足したい
メソッドがテーマ
このメソッド自体は一応目的を達成しているが、
不満点が山とある状態からスタート
- 複雑度が高すぎるし見通しが悪すぎる
- BookApp以降、本リストに重複がないことの保証は都度行う必要がある
- 状態依存が大きく、何か他の条件等を追加するときの改修が非常にやりにくい
あるサービスにおいて、日付文字列を取得するためのAPIが一つ定義されている。
※ここでのAPIはコントローラを模したアプリケーションクラス DateApi とする。
- 現時点での実日時を返却するAPI
- ISO8601拡張形式(秒まで)の文字列で返却する
- 例: 1985-10-26T01:35:00
- ISO8601拡張形式(秒まで)の文字列で返却する
ここに、次の新しいAPIを追加したい。
- 現在の営業日付、および現在営業中であるかを返却するAPI
- 例: 1985年10月25日 / false
APIの定義はスタブとして用意されたものを利用して、実装を付与してみよう。
- 業務知識をクラスに落とし込むにあたって、概念図を作成する
- 概念図に基づきメソッドを実装し、UTでその動作を保証する
- スタブメソッドを実装する
- スタブメソッドのUTを実装する
- 既存メソッドのUTを実装する
- 既存メソッドをリファクタする
- 日付の扱いについてはOffsetDateTimeとAsia/Tokyoのタイムゾーンを利用する。(改修前実装参照)
- 実プロダクトでは詳細に考慮すること
- 参考: https://qiita.com/dmikurube/items/15899ec9de643e91497c
- このサービスの「実日時」では、日付の秒数を「切り捨て」て扱うことになっている。
- 実日時と言いつつ生の日時データでない点に注意
- 「営業日時」について
- 実日時と異なり、秒数は「繰り上げ」て処理することになっている。
- 実日時と異なり、午前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日 | 営業日付が実日付と一致する時刻 |
複数のイベントを登録可能で、かつそれぞれのイベントに複数のコールバックを登録可能な
RunnerBrokerインターフェースを定義しておいたので、
実装クラスを作成の上、作成済みのRunnerBrokerTestで検証しよう。
Runnableではなく、Consumerを登録できるケース。
インターフェースやテストの定義はないので自分でなんとかしよう。
2の実装の時点で、イベントの連鎖が可能になっている。
そこで、処理に待ちが発生してしまうConsumerばかりが登録される前提で、
全体的な待ち時間の無駄をできるだけ押さえられるようなBrokerを実装しよう。
なお、待ちが発生するConsumerはThread#sleepでシミュレートしてよい。
通常、イベントを連鎖させるにはConsumer内でブローカーに対して
subscribeなりpublishなりを行う必要があるが、
Functionの戻り値をブローカーに与えるような書き方はできないだろうか?
- ブローカー側のインターフェースを工夫することになるだろう。
- もちろん、待ちが発生することを想定したい。
- 後段のイベントが続く前提のFunctionばかりではないことに注意しよう。
- 単独で終了するイベントのコールバックはConsumerで受け取っても良いかもしれない。
このモジュールは実装が間違っているうえにテストが書けない。
(特殊なモックライブラリを使わない限り。)
- テストできるように改修し
- 間違いを見つけて修正しよう
ヒント
- 参照透過性の有無で処理を分けるとよい。
- テストケースは細かく分けて3~4メソッド実装するとよい。
brokerパッケージで練習した処理で待ち時間の無駄を押さえる方法は練習した。
が、アプリケーションの規模が大きくなってくると自力での管理も大変になるし、
実装に機械の都合が含まれるようになってくるのも嬉しくない。
そこでそういうことが得意なReactiveXのJava実装の一つ、RxJavaライブラリを利用する。
(ちなみに特別な事情がなければプロダクトではProject Reactorを利用したほうがよい。
通常Javaでリアクティブプログラミングを行う際はSpring WebFluxが簡単だが、
これはProject Reactorを前提にするため。)
簡略化された梅干し製造工程をRxJavaを利用して表現してある。
まずは処理の流れを追い、何が起きているかを理解しよう。
工程の所要時間などをいじってみるのもよい。
梅製品の箱詰めを同様に作ろう。
箱詰めのモデルや工程は実装済みなので、以下を考えてうまく箱詰めを出力する仕組みを考えよう。
- どのように各工程に梅を分配するか
- どうやって1セット分の在庫が揃ったと判断するか