SOLID原則 - CASru-GAME/TeamGameDevBootcamp GitHub Wiki
https://zenn.dev/twugo/books/21cb3a6515e7b8/viewer/9d1da8 を見ながら読んでください
マーマイト訳は個人の理解なのであまり信用しすぎないこと(間違ってたりわかりにくかったりしたら指摘してください)
クラスは、1つの理由、単一の責任のみで変更されるべきだ。 各モジュール、クラス、関数が1つのことに責任を持ち、ロジックのその部分のみをカプセル化することを述べている。 複雑化を避けるため、厳密なルールはないが200~300行くらいが上限だと思ったほうがいい。
(マーマイト訳)クラスの多機能化を避けよう。できるだけパーツを分けて実装しよう。ただしやりすぎは禁物。
(具体例) playerのクラスの中に移動する、SEをならす、エフェクトを出すといったメソッドを作るのではなく、それぞれを別のクラスに分けて記述しplayerのクラスでは別個で作ったクラスを呼び出しているのみのほうが望ましい
・クラスが短くなることで説明や理解がしやすくなる
・再利用、拡張がしやすくなる
過剰にこの原則を適用すると、1つのメソッドしか持たないクラスを作るというようなことになるので常識との兼ね合いは必要。実装が簡単であることとシンプルであることは異なるので肝に銘じること。
クラスは拡張に対しては開いており、修正に対しては閉じているべきである。元のコードを変更することなく新しい動作を作成できるようにクラスを構成する。
(マーマイト訳)機能を拡張する際に、既存のコードを書き換えなくても拡張できるようにしろ。なぜなら機能拡張して不具合が起きたら新しい機能の部分だけを見ればよくなるから。インターフェースと抽象クラスを活用しよう。
インターフェースと抽象クラスの違い
どちらもメソッドの定義をして、継承したクラスにそのメソッドの実装を強制することができる。 インターフェースでは、メソッド名のみが定義できるのに対して、抽象クラスでは中身の実装まで定義できる。 インターフェースは複数継承できるのに対して抽象クラスは1つしか継承できない。 インターフェースでは、メソッドとプロパティのみを宣言するのに対して、抽象クラスは変数やフィールドを宣言・使用することができる。 インターフェースは、静的メンバ、コンストラクタ、アクセス修飾子(全部publicになる)を使用できないが、抽象はできる。具体例は元のテキストがわかりやすいのでそちらを参照
・バグが発生したら新しく追加した部分だけ見返せばよいので、デバッグが簡単になる
・コードの追加が長期的に簡単になる。
サブクラスは基底クラスに置換可能でなければならない。
もしサブクラス化の際に機能を削除している場合、おそらくリスコフの置換原則に反している。
基底クラスにロジックを追加すればするほど、リスコフの置換原則を破ることに近づく。
サブクラスは基底クラスと同じ public メンバ(関数、変数)を持つ必要がある。
現実の分類は、必ずしもクラス階層に変換できるとは限らない。
継承により機能を渡す代わりに、インターフェースや別のクラスを作り、特定の振る舞いをカプセル化する。
(マーマイト訳)派生クラスで実装する際、派生元のクラス(上の文章でいう基底クラス)が持つ機能は、派生先のクラス(サブクラス)に完全に共通する機能だけである必要がある。派生元のクラスに機能はたくさん持たせないほうが良い。機能の実装にはインターフェースや別のクラスを活用して組み合わせよう。現実での区分を考えるより、機能での区分を優先しよう。
・クラス階層の拡張性と柔軟性が高まる。
・継承を適切に使わないと、不必要に複雑化する。
・現実の分類が必ずしもクラス階層に変換できるわけではない
どのクライアントも、使わないメソッドに依存することを強制されるべきではない。言い換えると、巨大なインターフェースは避けるということ。
(マーマイト訳)単一責任原則と同様。インターフェースも大きなものを1つ作るより分割し、必要なものを組み合わせて使うほうが良い。ただし、これも多分やりすぎは厳禁。
・柔軟性を最大限に高め、インターフェースをコンパクトにして集中させることができる。
高レベルのモジュールは低レベルのモジュールから直接何かを取り込むべきではない。どちらも抽象化されたものに依存する必要がある。
あるクラスが他のクラスと関係を持つとき、それは依存関係または結合を持つ。理想は、クラス間の依存関係を可能な限り少なくすることであるため結合は少なく、コードを内部で機能する状態が望ましい。
(マーマイト訳)クラス同士の依存関係にルールがないとわかりにくくなってしまうのでルールを作る。クラスはできるだけ内部で完結し、ほかのコードと依存が発生する部分は少ないほうが良い。より抽象的なクラスを高レベル、具象的なクラスを低レベルとする。高レベルなクラスが低レベルなクラスに依存していると、拡張や修正の際に元のコードをいじる必要性が出てくる。それは開放/閉鎖原則に反するので、間にインターフェースのように抽象的なものを挟むことで、高レベルなクラスが低レベルなクラスに依存することを避けることが望ましい。これにより、低レベルなクラスに変更が生じても、高レベルなクラスに影響が及びにくくなる。
・高レベルクラスの拡張性が高まり、低レベルクラスの変更による影響を受けにくくなる。
・プロジェクトを便利に拡張、縮小できる。