ひよっこゲームブログ

なにもかも初心者のひよっこがゆったりと何かする

CleanArchitectureの話

記事を書くのに間が空き過ぎてしまった・・・
最近天則に復帰したりVLORANTが楽しかったりで忙しいです
転職したいならその時間を勉強にあてろ


CleanArchitectureの勉強はぶっちゃけ手間取りました
DDDもかじりながらボブおじのブログを読んだので・・・
あと原文をそのまま理解しようとするのは難易度が高い
そもそもCleanArchitecture自体がちょっと難しい・・・(個人差があります)


正直なところ記事にするけど内容の正確性に自信がない
まさかり募集


blog.cleancoder.com


CleanArchitectureを一言でまとめると
「抽象度でレイヤー分けして、依存性を排除したアーキテクチャ

具体的なモノ(抽象度が低いもの = UI, DB, etc...)は変更されやすいので
そこは一番いじりやすくしようぜ!みたいなニュアンスだと思ってます

ここからは和訳もかねて書いてく


みんな大好きなあの図。ボブおじのブログから引用

f:id:denden_kata:20200607222818j:plain


様々なアーキテクチャがあるが基本的に目的は同じであり
「ソフトウェアを機能ごとにレイヤー分けすることで関心の分離を行う」ことにある

  1. フレームワークに依存しない
    制限された状態でシステムに機能を落とし込む必要性が無くなる

  2. テスト可能
    外部に依存しないのでテストができる

  3. UIに依存しない

  4. DBに依存しない

  5. 外部から独立している。ビジネスルールは外部の状態を知らなくても良い

依存関係のルール

外側から内側に向けて依存する
内側はが外側の状態を知らなくても良い


エンティティ

不変なビジネスルールを記述
サービスの基幹部分(単一のアプリケーションであれば、アプリの仕様になる)
要はこれがなきゃサービスにならないレベルのもの(=ドメインオブジェクト )

ex.) twitterであれば「ユーザー」や「ツイート機能」などに当たる

外部の変更があった場合でも、変更される可能性が最も低い


ユースケース

エンティティに記述しないようなビジネスルールはこっちに記述する
ソフトウェアの仕様変更の影響を受ける

このレイヤーはエンティティへ影響を与えることは想定していない(無関心)
エンティティとのデータフローを調整
ユースケースの目的を達成できるようにエンティティへ指示する


インターフェースアダプター

エンティティやユースケースのデータを、DBなどで使える形に変換する
エンティティとユースケースはDBについては知らなくて良い

ようはぶっちゃけViewMode強化版みたいなとこ


フレームワークとドライバ

DB・UI・Webのフレームワーク
変更の多いところはここ


図について

あくまで例として4つのレイヤーにしただけで、増えることもある
仮に増えても依存性は外側から内側へなのは変わらない
内側へ向かうとソフトウェアはより抽象的になる


レイヤーの境界を超える

右下の図について説明

ユースケースと通信するコントローラーとプレゼンター
コントローラで始まりユースケースを内でデータを操作、プレゼンターで実行

普通は「依存性逆転の法則」を利用するんだとかなんだとか

直にやりとりすると依存性が高いので×


境界を超えるデータ

境界を越えてデータを渡す場合
常に内側の円に最も便利な形式で渡す
内側の円は外側の状態を意識しなくて良い!


個人的な感想

実装の仕方やモノによって、様々なデータの流れがあって複雑になりそう

抽象度が高い部分で、ロジックをどこに書くかというのが曖昧になりそう
別途ルールを作らないと、人によって書き方がバラバラになりそうかなー・・・?

ちょっとまだ理解がふわふわしてる

UIが一番上で、DBが一番下なアーキテクチャに馴染みがあるので理解するのに時間がかかった

リアクティブプログラミング10日目

結構間が空きました。元気になったので再開
無理はよくない、転職してぇ、そのためのRxJava

最近アプリ開発から強制立ち退きカウントダウンを食らって気分が沈んでます
自分が関わっているサービスが今後ボロボロになっていくと思うと辛い

別に僕が何かやったとかそういうのが理由ではないのが本当に悔しい

Base class vs base type

性能比較でもすんの?
ざっと和訳

RxJavaの設計はReactive Streamsの仕様の影響を強く受けたため
ライブラリには各々にインターフェースが用意されている

「基本クラスが静的メソッドとインスタンスメソッドが多いので重い」
という記述があるが何の意味かわからん・・・

「considered heavy」って「重いと考えられる」だよな
重いって何?何が重いん・・・?


リファレンスに載っている表
「各々にインターフェースが用意してあるよ」ってことで記述されてるんだけど・・・

Type Class Interface Consumer
0..N backpressured Flowable Publisher Subscriber
0..N unbounded Observable ObservableSource Observer
1 element or error Single SingleSource SingleObserver
0..1 element or error Maybe MaybeSource MaybeObserver
0 element or error Completable CompletableSource CompletableObserver


Consumer is 何


表のTypeとclassで説明されていることはこれで説明した表の詳細

denden-kata.hatenablog.com

この表ですね

名前 説明
Observable 値が複数
Single 値が1つ
Maybe 値が1つ or なし
Completable 値なし

FlowableとObservableはバックプレッシャーがあるないだけ


んで、後ろの部分は何なんだ
Reactive Streamsの仕様を見てみる

(抜粋)
SPECIFICATION
1.Publisher

public interface Publisher<T> {
    public void subscribe(Subscriber<? super T> s);
}

あっこれかぁ

つまるとこインターフェースで呼ぶときはこうしろってことなんですかね
ちょっと良く分かんないです
というかこれ何の意味があるんですか(殴られそう)

RxJavaではなくてリアクティブプログラミングを根本から勉強しろと言われてる気がする

RxJavaトップページの翻訳はこれでおしまいです
あとは下の方に別のドキュメントとかwikiがあるのでそれを見ろという感じで・・・


総括

RxJavaの基本はコレクションをストリームで管理するという非常にシンプルなもの
多分これでいいと思います

ぶっちゃけ基本を学ぶだけでも覚えることめっちゃ多いのかと思っていたけど
いざ取り掛かってみるとそこまででもなかった

概念は結構シンプルで分かりやすい
まぁ実際に現場で使ってみたりすると沼にハマったりするんだろうけど・・・

Android開発で利用する場合はライフサイクルとかUIスレッドとか
色々と考えながら実装しないと思った通りに動いてくれないみたいな事故があるんですよね
変な実装しなければ割ときちんと動くはずなんですけど・・・

結構適当に読み解いているのでそろそろ警察に後ろから刺されそう


せっかくのGWなので
次はなんかいいサンプルコードがあれば復習がてら読み解いていくか
Clean Architectureの勉強をしていこうと思いまーす



おしまい

リアクティブプログラミング9日目

あーるえっくす じゃばーーー

Operatorの命名規則

使用できないキーワード

元のRx.NETで使ってた名前はJavaですでに使われていたのでダメだったという話

singleなアイテムを発行~完了するOperatorはReturn(T)という名前だった
return(T) ⇒ just(T)

そのほか
switch ⇒ switchOnNext
Catch ⇒ onErrorResumeNext


型消去

型消去 is 何?

型消去 = 「イレイジャ」と呼ばれる
Javaの機能でこれについて書くと記事が1つできるレベルなのではしょると

コンパイラによってパラメータ化された型や型引数を含まない型に変換された物(あいまい)
ex.) List<String>のイレイジャはList

型消去で色々起こるので、まぁ何とかしようとかそんな感じの項目
下記のコードはダメみたい

サンプルコード

Flowable<R> flatMap(Function<? super T, ? extends Publisher<? extends R>> mapper)

Flowable<R> flatMapMaybe(Function<? super T, ? extends MaybeSource<? extends R>> mapper)



Java8とラムダを使っている場合は曖昧で色々起こる可能性がある(キリがないのではしょります)

サンプルコード(concatWithの場合)

Flowable<T> concatWith(Publisher<? extends T> other);

Flowable<T> concatWith(SingleSource<? extends T> other);



ラムダ式のインターフェース

サンプルコード

someSource.concatWith(s -> Single.just(2))
.subscribe(System.out::println, Throwable::printStackTrace);



このときSingle.just(2)は機能しない
concatWithのOverloadの種類が多すぎてコケる

正しくは下記のように書く

サンプルコード

someSource.concatWith(Single.defer(() -> Single.just(2)))
.subscribe(System.out::println, Throwable::printStackTrace);



場合によってはフローでもコケるので下記のように書く

サンプルコード

Flowable<T> merge(Publisher<? extends Publisher<? extends T>> sources);

Flowable<T> mergeArray(Publisher<? extends T>... sources);



うーーーーーん。理解度が微妙だけど言いたいことはなんとなくわか・・・る・・・?
なんかこれも詰まった時ににらめっこ案件だな

正直今までで一番内容がふんわりしている・・・
理解したとは言い難いが立ち止まっても進まないので頭の片隅に置いておこう

今日はちょっとここまで、続きは明日

リアクティブプログラミング8日目

今日もRxJava

型変換

ソースやサービスがそこで動作するフローと異なる型を返すことがある

解決手段は2通りあって

1. 目的の型へ変換する
2. 異なる型をサポートする特定の演算子を使う

まぁなんかここははリファレンスを参照としか言いようがない・・・

各使い分け
FlowableはバックプレッシャーがついてるObservableなので説明省く

名前 説明
Observable 値が複数
Single 値が1つ
Maybe 値が1つ or なし
Completable 値なし


目的の型へ変換

RxJavaでは変換を実行して他の型へ一致させる演算子がデフォルトで実装されてる
表は利用可能な変換オプション一覧

Flowable Observable Single Maybe Completable
Flowable toObservable first, firstOrError, single, singleOrError, last, lastOrError(※1) firstElement, singleElement, lastElement ignoreElements
Observable toFlowable(※2) first, firstOrError, single, singleOrError, last, lastOrError(※1) firstElement, singleElement, lastElement ignoreElements
Single toFlowable(※3) toObservable toMaybe ignoreElements
Maybe toFlowable(※3) toObservable toSingle ignoreElements
Completable toFlowable toObservable toSingle toMaybe

※1 multi-valuedをsingle-valuedにする場合は、どの値を結果として返すか考える必要がある
※2 場合によって振る舞いを変更する必要性があるらしい
※3 ソースアイテムが1つの場合はバックプレッシャーを気にする必要はない


Overloadの利用

別の型へ変換してくれるOverloadなやつら

Operator Overloads
flatMap flatMapSingle, flatMapMaybe, flatMapCompletable, flatMapIterable
concatMap concatMapSingle, concatMapMaybe, concatMapCompletable, concatMapIterable
switchMap switchMapSingle, switchMapMaybe, switchMapCompletable


場合によって使い分けていけって感じだけど、そこそこ分かりやすい。ふむふむ
ここんとこ夜更かしばっかでよくなーい、寝る

リアクティブプログラミング7日目

今日もRxJavaのリファレンス読む

Continuations

和訳で「継続」


Dependent

和訳で「依存」

itemが利用可能になったとき、それに依存する計算をしたい
これをContinuationsと呼ぶ

最も一般的なパターンは
値を指定して別のサービスを呼び出し、結果を受け取って処理を継続するとか

サンプルコード

service.apiCall()
.flatMap(value -> service.anotherApiCall(value))
.flatMap(next -> service.finalCall(next))

サンプルコード見た感じだとまぁ見たまんまの処理


flatMapの中にflatMapを入れるみたいな場合

サンプルコード

service.apiCall()
.flatMap(value ->
    service.anotherApiCall(value)
    .flatMap(next -> service.finalCallBoth(value, next))
)

これもシンプルに見たまんまの処理


Non-dependent

和訳で「非依存」

原文読んでもピンとこなかったので読み解いてみる

サンプルコード(ダメなパターン)

Observable continued = sourceObservable.flatMapSingle(ignored -> someSingleSource)
continued.map(v -> v.toString())
  .subscribe(System.out::println, Throwable::printStackTrace);
  • flatMapSingle
    Flowableの各要素をSingleSourceにしてマッピングする
    Single = 値かエラーが1度だけ流れる

今までと違うところは、observableの宣言をしてflatMap
そのあとにmapで整頓してる

1回ずつoperatorを使いたいって感じ

この場合だとoperatorの処理でマッピングが複数の値をもたらす可能性があるみたい
んー、なぜだ?

あー・・・?
値が入り次第、次のmapが動作しちゃうみたい

こっちの方法で書くのが正解

andThenを利用することで、前のobservableが正常完了してから次のobservableが動作する
なるほど

サンプルコード

sourceObservable
  .ignoreElements()           // returns Completable
  .andThen(someSingleSource)
  .map(v -> v.toString())


Deferred-dependent

和訳で「遅延依存」

前と後ろでデータ依存があり
何らかの理由で「通常のチャンネル」をストリームしていない場合

サンプルコード(ダメなパターン)

AtomicInteger count = new AtomicInteger();

Observable.range(1, 10)
  .doOnNext(ignored -> count.incrementAndGet())
  .ignoreElements()
  .andThen(Single.just(count.get()))
  .subscribe(System.out::println);

これは全て「0」が出力されてしまう
Single.just(count.get()) がデータフロー実行前に評価されてしまうのでアウト

よってメインのストリームが終わってから評価する必要がある
=遅延依存


サンプルコード

AtomicInteger count = new AtomicInteger();

Observable.range(1, 10)
  .doOnNext(ignored -> count.incrementAndGet())
  .ignoreElements()
  .andThen(Single.defer(() -> Single.just(count.get())))
  .subscribe(System.out::println);

または

AtomicInteger count = new AtomicInteger();

Observable.range(1, 10)
  .doOnNext(ignored -> count.incrementAndGet())
  .ignoreElements()
  .andThen(Single.fromCallable(() -> count.get()))
  .subscribe(System.out::println);


あー、これは忘れたころにつまずきそうなパターンだ・・・


今日やった部分はデータフローの仕方がごちゃっとしてるなぁ(体感)
とりまここまで、おやすみなさーい

リアクティブプログラミング6日目

Dependent sub-flows

和訳で「依存サブフロー
依存サブフロー is 何

本文を読む

flatMapは強力なoperatorです
お、おう

サンプルコード

Flowable<Inventory> inventorySource = warehouse.getInventoryAsync();

inventorySource
    .flatMap(inventoryItem -> erp.getDemandAsync(inventoryItem.getId())
            .map(demand -> "Item " + inventoryItem.getName() + " has demand " + demand))
    .subscribe(System.out::println);

んー、コード見てもぱっと分からん

調べてみる

ReactiveX - FlatMap operator
説明を読んだ感じだと
map(itemを変換)とmarge(複数動作していたObservableをまとめる)が合体したみたいな

図を見る限り変換後の順番は保証してないっぽいね


次の項目がちょっと長いので、ちょっと短めでおしまい

今日はちょっと仕事もあったので精神的にもんにょり
リモートワーク始まってから休日に労働をせがまれるリスクを背負ってるなぁと

寝るーん

リアクティブプログラミング5日目

Concurrency within a flow

和訳で「フローの並行性」

サンプルコード

Flowable.range(1, 10)
  .observeOn(Schedulers.computation())
  .map(v -> v * v)
  .blockingSubscribe(System.out::println);

バックグラウンドで1~10を2乗していって、結果をメインスレッドで返す

普通

Parallel processing

並列処理

Flowable.range(1, 10)
  .flatMap(v ->
      Flowable.just(v)
        .subscribeOn(Schedulers.computation())
        .map(w -> w * w)
  )
  .blockingSubscribe(System.out::println);

RxJavaの並列処理は 「独立したフローを実行してその結果を単一のフローにマージする」だそうな

なるほど。

flatMapは順序を保証するものではないそうです
なので代替のものがある

  • concatMap
    一度に1つのフローを実行する

  • concatMapEager
    すべてのフローを一度に実行するが、出力フローは処理された順番になる

concatMapEagerはちょっと行儀悪いね


こう書いてもいいって

Flowable.range(1, 10)
  .parallel()
  .runOn(Schedulers.computation())
  .map(v -> v * v)
  .sequential()
  .blockingSubscribe(System.out::println);

ちょっと待て出てきてないものが多い
parallel() = ParallelFlowable
runOn
sequential()

parallel()で並列であることを明記して
runOn()でスケジューラを設定
sequential()は最初に明記した処理順に単一のフローへマージする(図が分かりやすい)


今日はずいぶんのんびりやってしまった