コーディング
作成日時:2024-08-01以前
更新日時:2024-09-29
WIP.
PIE原則絶対主義
Program Intently and Expressively.
直訳:意図的かつ表現力豊かにプログラミングする。
全てはPIE原則に基づきコーディングを行う。
可読性高く、かつ認知負荷の低いコードを書かなければならない。
読む人間の事を第一に考えなければならない。
確実に意図を相手に伝える。
意図が読み取れないコードは全てクソコード。
CLEAN原則
- Cohesive (凝集性)
- Loosely Coupled (疎結合)
- Encapsulated (カプセル化)
- Assertive (自己主張的・断定的)
- Non-redundant (非冗長)
原則の順守
コーディング原則は順守しなければならない。
但し、納得できる理由を説明できるならば、守らなくてもよい。
- DRY/OAOO
- KISS
- TUBE
- シンプルさの尺度
- Testable(テスト可能)
- Understandable(理解可能)
- Browsable(発見可能)
- Explainable(説明可能)
- TUBE
- YAGNI
- SOLID
- SLAP
- PIE
- CLEAN
- デメテルの法則
- 小は美なり
- 名前重要
- 高凝集・疎結合
DRY原則とOAOO原則
1. 定義と基本概念
DRY (Don’t Repeat Yourself)
- 意味:システム内のあらゆる知識の重複を避ける
- 範囲:コード、ドキュメンテーション、データベーススキーマなど、システム全体
- すべての知識はシステム内において、単一、かつ明確な、そして信頼できる表現になっていなければならない。(達人プログラマーより引用)
OAOO (Once And Only Once)
- 意味:コードの中で同じことを一度だけ書く
- 範囲:主にコード内の重複に注目
2. 主な違い
- 適用範囲:DRYはより広範囲(システム全体)、OAOOは主にコードに焦点
- 重複の定義:DRYは知識や情報の重複、OAOOはコード断片の重複
- 包含関係:DRYはOAOOの概念を包含していると一般的に考えられる
たとえば、DRY はファイルの集合の場所はアプリケーションの中で一箇所に格納されるべきと主張するが、これらのファイルからデータを取り出す処理をアプリケーションの異なる箇所で記述する回数については寛容である。OAOO の原則は、これに対して、ファイルからデータを取得するコードは一度のみ書かれるよう求めるが、アプリケーション内でこれらのファイルを配置する場所が何箇所あるかについては寛容である。
Don’t repeat yourself - Wikipediaより引用。
DRYは「情報が単一の場所にまとまっていること」。
DDD的。
OAOOは「コードの重複が無いこと」。
3. 具体的な適用例
DRY
- 設定情報の一元管理
- APIドキュメントの自動生成
- データベースの正規化(One Fact in One Place)
- 導出可能なデータ
- 「単価 * 個数 = 合計料金」の合計料金を保持しないとか。
- ビジネスロジックの共有(フロントエンド/バックエンド間)
OAOO
- 共通の計算ロジックを関数にまとめる
- 条件分岐の重複を避ける
- ユーティリティ関数の作成
4. 関心事の分離との関係
- DRY原則の適用は多くの場合、関心事の分離を促進する
- 共通機能の抽出によって、自然と責任が分離される傾向にある
- 例:データベース接続、バリデーションロジックの分離
5. 注意点
- 過度な適用は避ける(特にDRY)
- コンテキストを考慮し、適切なバランスで適用する
- 小さすぎる機能の分離はかえって複雑さを増す可能性がある
説明責任
自分が実装したコードは説明できなければならない。
コピペコードやAIが出したコードも許容するが、説明できないならばそのコードはクソ。
但し、納得できる理由を説明できるならば、守らなくてもよい。
用語の統一
ユビキタス言語を作成しなければならない。
シノニム・ホモニム・表記ゆれはバグとストレスの温床。
DB定義書が存在するならば、そのファイルから物理名と論理名を抜き出し、
コーディングにおいてはそれらを使用するとよい。
「後で直す」は永久に直されない
気付いたならば今すぐ直せ。
コメントは「何故」を書け
何をしているかはコードを読めばわかる。
「何故」が分からなければリファクタリングが面倒になる。
読み取れない情報を書け。
技術
- リンタ・フォーマッタ
- 警告全部消せ
- 割れ窓理論と技術的負債
- 列挙型使え
- シノニム・ホモニム・表記ゆれは止めろ
- ユーザからの入力値を信用しない
- システムが遅いのは大体DBのせい。
- パフォーマンス試験で非機能要件を満たせないとめんどくさい
- 不要なロジックはコメントアウトで消すな
- コマンド・クエリ分離の原則
- クエリはいつでも呼べるようになる。副作用が無いため。
- 不変性と状態の不保持
変数名を省略してはいけない
変数名には、その変数の役割や意味を簡潔に表す命名規則を守ることが重要だ。
適切な変数名は、コードの可読性を高め、メンテナンス性を向上させる。
一方で、メソッドが長くなりすぎる場合は、メソッドの抽出を検討する必要がある。
メソッドを小さな単位に分割することで、コードの可読性と保守性が向上する。
メソッドを抽出した結果、メソッド名から連想される内容が変数名に反映されるため、変数名を短くできる。
スコープが短ければ簡易的な変数名でもよい。
高凝集にすれば、変数名を考える手間を省ける。
ク◯コードはク◯コードを生む
認知負荷が上がる。つまり脳のCPUを無駄に使わされるから。
割れ窓理論。
外部ライブラリ
ラップして使用する。
要するにそのライブラリを使用する専用のクラスを作成する。
ライブラリを変更する際の改修が、そのクラスだけで済む。
一種のアダプターパターン。
使用箇所がライブラリに依存するのではなく、使用箇所がライブラリをラップしたクラスに依存する。
依存が多いのはどっち。前者。
変更容易性
関心の分離
意図的な密結合
- 密でなければならないとか。
- そのオブジェクトが使用されていなければならない
- 疎結合にする必要が無い場合とか。
- 実装が1個しかない
型による制限
デザインパターン
GoFの23パターン
生成に関するパターン
- Abstract Factory
- Builder
- Factory Method
- Prototype
- Singleton
構造に関するパターン
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
振る舞いに関するパターン
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
FacadeとAdapter(Claude)
目的
Facade: 複雑なサブシステムに対してシンプルなインターフェイスを提供し、使いやすくすることが目的です。
Adapter: 互換性のないインターフェイスを持つクラスを協調して動作させることが目的です。
インターフェイス
Facade: 新しい、シンプルなインターフェイスを作成します。
Adapter: 既存のインターフェイスを別のインターフェイスに適合させます。
複雑さの扱い
Facade: 複雑さを隠蔽します。
Adapter: 複雑さを変換します。
使用場面
Facade: 複数のクラスや複雑なサブシステムを扱う際に使用します。
Adapter: 既存のクラスを新しいインターフェイスで使用したい場合に使用します。
関係するクラス数
Facade: 多くの場合、複数のクラスを扱います。
Adapter: 通常、1つまたは少数のクラスを扱います。
ロックの獲得
ロックの実装はいろいろ。
- DB
- Redis
- ファイル
- 言語(synchronized)
MVC
よほど大規模でなければ、1API-1Controller-1Serviceでいいと思う。
単一責務の原則。
1ファイルにまとめた場合、Gitだとコンフリクトが多発する可能性。
食べログとか、1エンドポイント1クラスらしい。
Software Design 2024年10月号のP.41
リファクタリング
小さい粒度でコツコツと
CSVで考える事
- ファイル名
- 文字コード
- BOMの有無
- 区切り
- 囲み文字
- 改行コード
- エスケープ
- ヘッダー
- ファイルサイズ
- ブランクとNULL
- CSVの使用目的
不変性
- 全てfinal
- コンストラクタで参照を渡されたらコピー
- getterはコピーを渡す
- setterなし
recordを使えばprivate finalだけど、アクセスがgetXXXじゃないのがヤダ。
値によってインスタンス切り替え
愚直にifかClass.forName。
分岐はファクトリー内で完結させろ
設計書は必ず腐る
だからコードは自己説明的でなければならない
アクセス不可のメソッドを無理矢理呼ぶ方法
こうすれば外部から不可視なメソッドを外部から呼べる。
良くないけど。
どうしても内部のものを呼びたい時にだけ使用する。
protected int method(){...}
public int wrapper() {
return this.method();
}