takeshi-1000 / my_memo

0 stars 1 forks source link

Clean Architecture 達人に学ぶソフトウェアの構造と設計 メモ #65

Open takeshi-1000 opened 2 years ago

takeshi-1000 commented 2 years ago

緊急と重要のマトリクス

  1. 緊急で重要
  2. 緊急ではないが重要
  3. 緊急ではあるが重要ではない
  4. 緊急でも重要でもない

3のものをしばしば、1に昇格させてしまい、コードのアーキテクチャが蔑ろにされてしまう

takeshi-1000 commented 2 years ago
takeshi-1000 commented 2 years ago

プログラミングの三つのパラダイム

takeshi-1000 commented 2 years ago
takeshi-1000 commented 2 years ago

プログラムが正しくないことをテストで証明する → 証明できないからプログラムが正しい

証明可能な機能に分割する

takeshi-1000 commented 2 years ago

オブジェクト指向 → オブジェクト指向でない言語を、オブジェクト指向ライクに書くことは可能そうであるが(C言語) 安全に、そして効率よく実装することが難しそう

takeshi-1000 commented 2 years ago

ポインタ

ポインタとは、変数が格納されているメモリ上のアドレスを指し示す

C言語で実装すると下記のような感じっぽい

#include <stdio.h>

int main(void) {
    int num = 1; // int型変数
    int *p_num; // int型ポインタ変数
    p_num = &num; // ポインタ変数p_numに変数numアドレスを代入

    printf("int型変数numの値:%d\n", num);
    printf("int型ポインタ変数p_num:%p\n", p_num);

    return 0;
}

イメージ掴むならこの辺 https://wa3.i-3-i.info/word12814.html

takeshi-1000 commented 2 years ago

C言語も高級言語と比べるとより低レイヤーに近い部分で、 高級言語には呼べないのかも

機械語 < アセンブリ < C言語 < 高級言語

システム言語って呼ぶのかな https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E

takeshi-1000 commented 2 years ago

安定依存の原則 (SDP: Stable Dependencies Principle)https://at-grandpa.hatenablog.jp/entry/dip#:~:text=%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82-,%E5%AE%89%E5%AE%9A%E4%BE%9D%E5%AD%98%E3%81%AE%E5%8E%9F%E5%89%87%20%EF%BC%88SDP%3A%20Stable%20Dependencies%20Principle%EF%BC%89,-%E8%A8%AD%E8%A8%88%E5%8E%9F%E5%89%87%E3%81%AE

安定したモジュールに依存するように設計する

レイヤードアーキテクチャは、 システムにおけるプログラムをいくつかの コンポーネントに分け(プレゼンテーション、ドメイン、APIレイヤー云々) より安定したものを真ん中に寄せ、 真ん中に向かって、コンポーネントをつなぎ合わせる A -> B B -> C C -> D

ここでいう安定は、プロジェクトにおいて様々だと思うが 一般原則的にドメインレイヤがより相対的に安定したものとしたものとして 合意とっている感ある?

takeshi-1000 commented 2 years ago

依存性逆転の原則において重要なこと、見落としがちな視点

抽象に依存するように実装すべきだとは思うが、 抽象が頻繁に変更されないような制約が必要そうなこと

抽象の変更は、依存する側と、 抽象に準拠したもの二つに影響を加えることは ~トレードオフとなると考える~

具象 -> 具象で依存させた時の 変更度合いと同じである。むしろソースコードのファイルチェンジ数で行けば、 抽象に依存させた方がコードの変更量が多そう

この辺りの前提はあるものの、 具象の変更の方が抽象の変更に比べて頻繁であるため 抽象に変更が加わった場合のデメリットよりも 抽象に依存して、具象ごとの変更で変更の影響を少なくすることをとっている?

takeshi-1000 commented 2 years ago

言語機能として

具象に依存するのではなく、抽象に依存する ように実装できる場合は極力そのようにし、 抽象への変更はできるだけ少なくする

takeshi-1000 commented 2 years ago

依存性の注入

前提としてどのオブジェクトも抽象に依存しているような状態にしておく

class A: Alable {

let b: Blable

init(b: Blable) {
 self.b = b
}

}

class B: Blable {

}

protocol Blable { }

仮に抽象に依存したプロパティを持つオブジェクトTopObjectを考えてみる

class TopObject {
  let a: Alable
  let b: Blable
  let c: Clable

  init(a: Alable, b: Blable, c: Clable) {
   self.a = a
   self.b = b
   self.c = c
  }
}

このオブジェクトの生成をどこで呼ぶのか?

例えば、EntryObectがTopObjectを呼ぶ必要があり、依存関係として EntryObject -> TopObject になるようにする必要がある場合

class EntryObject {

let topObject: TopObject

init(topObject: TopObject) {
 self.topObject = topObject
}

今やっている案件だと、 下記の configureWith メソッドのように 一番最初のエントリ?っぽいところでA, B, C を作成している これは、EntryObjectが、特に使用するわけでもない、a, b, c に依存している(aの変更、bの変更、cの変更に影響を受けうる)

class EntryObject {

let topObject: TopObject

static func configureWith() {
 let a = A()
 let b = B()
 let c = C()
 let topObject = TopObject(a: a, b: b, c: c)
  let entryObject = EntryObject(topObject: topObject)
 return entryObject
}

init(topObject: TopObject) {
 self.topObject = topObject
}

そうしないようにDIコンテナという、オブジェクトの生成をしつつ、 依存オブジェクトをまるっと生成する場所を一括管理しようね 的なことなのだろう

またこれはモジュールを独立させることで、 単体テストが可能になる…?

takeshi-1000 commented 2 years ago

依存云々のことを考える上では、下記のブログを踏まえるのが良さそう 今まで読んだ記事の中では順を追って理解しやすいものであった https://at-grandpa.hatenablog.jp/entry/dip

takeshi-1000 commented 2 years ago

依存関係の話を聞いて思ったのは、 まず抽象化したものを変更しないような 設計にすることが第一で、

その上で具象が抽象に依存しよう、 の流れになりそう

今やっている案件だと、

あるViewControllerが、CustomLabelを継承したHogeLabelを使用するという場面があり、 CustomLabelは他にも継承しているのにも関わらず、 ベース側のクラスにポンポン修正が入り、 継承先のラベルに影響を受け、 さらにはそれを使用しているViewControllerにも影響を受けるという箇所が散見される

一応流れとしては、 抽象クラスの期待される役割を明確にしつつ、 それを部分的にリプレイスするというのが必要そう

あるいはひとまず、部分部分でUILabelを継承したHogeLabelにしておき、 後から共通部分をCustomFontLabelとして集約するのが良いのかもしれない