Open takeshi-1000 opened 2 years ago
プログラミングの三つのパラダイム
プログラムが正しくないことをテストで証明する → 証明できないからプログラムが正しい
証明可能な機能に分割する
オブジェクト指向 → オブジェクト指向でない言語を、オブジェクト指向ライクに書くことは可能そうであるが(C言語) 安全に、そして効率よく実装することが難しそう
ポインタ
ポインタとは、変数が格納されているメモリ上のアドレスを指し示す
C言語で実装すると下記のような感じっぽい
#include <stdio.h>
int main(void) {
int num = 1; // int型変数
int *p_num; // int型ポインタ変数
p_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
C言語も高級言語と比べるとより低レイヤーに近い部分で、 高級言語には呼べないのかも
機械語 < アセンブリ < C言語 < 高級言語
安定依存の原則 (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
ここでいう安定は、プロジェクトにおいて様々だと思うが 一般原則的にドメインレイヤがより相対的に安定したものとしたものとして 合意とっている感ある?
依存性逆転の原則において重要なこと、見落としがちな視点
抽象に依存するように実装すべきだとは思うが、 抽象が頻繁に変更されないような制約が必要そうなこと
抽象の変更は、依存する側と、 抽象に準拠したもの二つに影響を加えることは ~トレードオフとなると考える~
具象 -> 具象で依存させた時の 変更度合いと同じである。むしろソースコードのファイルチェンジ数で行けば、 抽象に依存させた方がコードの変更量が多そう
この辺りの前提はあるものの、 具象の変更の方が抽象の変更に比べて頻繁であるため 抽象に変更が加わった場合のデメリットよりも 抽象に依存して、具象ごとの変更で変更の影響を少なくすることをとっている?
言語機能として
具象に依存するのではなく、抽象に依存する ように実装できる場合は極力そのようにし、 抽象への変更はできるだけ少なくする
依存性の注入
前提としてどのオブジェクトも抽象に依存しているような状態にしておく
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コンテナという、オブジェクトの生成をしつつ、 依存オブジェクトをまるっと生成する場所を一括管理しようね 的なことなのだろう
またこれはモジュールを独立させることで、 単体テストが可能になる…?
依存云々のことを考える上では、下記のブログを踏まえるのが良さそう 今まで読んだ記事の中では順を追って理解しやすいものであった https://at-grandpa.hatenablog.jp/entry/dip
依存関係の話を聞いて思ったのは、 まず抽象化したものを変更しないような 設計にすることが第一で、
その上で具象が抽象に依存しよう、 の流れになりそう
今やっている案件だと、
あるViewControllerが、CustomLabelを継承したHogeLabelを使用するという場面があり、 CustomLabelは他にも継承しているのにも関わらず、 ベース側のクラスにポンポン修正が入り、 継承先のラベルに影響を受け、 さらにはそれを使用しているViewControllerにも影響を受けるという箇所が散見される
一応流れとしては、 抽象クラスの期待される役割を明確にしつつ、 それを部分的にリプレイスするというのが必要そう
あるいはひとまず、部分部分でUILabelを継承したHogeLabelにしておき、 後から共通部分をCustomFontLabelとして集約するのが良いのかもしれない
緊急と重要のマトリクス
3のものをしばしば、1に昇格させてしまい、コードのアーキテクチャが蔑ろにされてしまう