プログラミングパラダイムの比較
手続き型、オブジェクト指向、関数型、宣言型の特徴とメリット・デメリットを比較する。
概要
手続き型、オブジェクト指向、関数型、宣言型は、互いに排他的な分類ではありません。
実際のアプリケーションでは、業務ルールをオブジェクト指向で整理し、データ変換に関数型の考え方を使い、SQLを宣言的に記述し、細かな処理を手続き型で書く、といった組み合わせが一般的です。
全体比較
| 観点 | 手続き型 | オブジェクト指向 | 関数型 | 宣言型 |
|---|---|---|---|---|
| 中心となる考え方 | 処理手順 | 状態と振る舞いを持つオブジェクト | 関数と値の変換 | 得たい結果や状態 |
| 主な構成単位 | 関数、手続き、変数 | クラス、オブジェクト、インターフェース | 純粋関数、不変値 | 宣言、規則、制約 |
| 状態変更 | 直接変更することが多い | オブジェクト内部へ閉じ込める | できるだけ避ける | 実行側へ任せることが多い |
| 処理順序 | 明示する | メソッド呼び出しとして表れる | 関数の合成として表す | 原則として細かく指定しない |
| 得意なこと | 短い逐次処理、アルゴリズム | 業務ルール、状態遷移、変更の局所化 | データ変換、計算、並行処理 | 検索、構成、UI、状態管理 |
| 注意点 | 可変状態と長大な処理 | 過剰な抽象化、責務の肥大化 | 学習コスト、副作用の扱い | 実行過程と性能が見えにくい |
メリットとデメリット
| パラダイム | メリット | デメリット |
|---|---|---|
| 手続き型 | 実行順序が明確で、小さな処理を理解しやすい | 共有状態や長い処理が増えると変更箇所を追いにくい |
| オブジェクト指向 | 状態とルールをまとめ、責務と変更範囲を整理しやすい | クラスや継承を増やしすぎると構造が複雑になる |
| 関数型 | 状態変更を減らし、テストや並行処理を扱いやすくする | 抽象化に慣れが必要で、現実の副作用は別途扱う必要がある |
| 宣言型 | 手順ではなく目的を簡潔に表し、実行側の最適化を利用できる | 実際の動作や性能問題を理解するには実行基盤の知識が必要になる |
Javaでの使い分け
Javaはオブジェクト指向だけで書く言語ではありません。
public Receipt calculateReceipt(Order order) {
int subtotal = order.getItems().stream()
.mapToInt(item -> item.getPrice() * item.getQuantity())
.sum();
int fee = subtotal >= 5000 ? 0 : 500;
return new Receipt(subtotal, fee);
}
この短いコードにも複数の考え方があります。
OrderとReceipt: オブジェクト指向- Streamによる合計: 関数型
- 送料を決める条件と処理順序: 手続き型
さらに注文をDBから取得するSQLは宣言型です。一つのシステム内で、それぞれの得意分野を組み合わせます。
判断の目安
手続き型を選びやすい場合
- 処理が短く、実行順序が重要
- 状態変更の範囲が狭い
- アルゴリズムを直接表現したい
オブジェクト指向を選びやすい場合
- 状態遷移に制約がある
- 業務ルールとデータをまとめたい
- 実装の変更をインターフェースの内側へ閉じ込めたい
関数型を選びやすい場合
- 入力から出力を求める計算が中心
- コレクションの変換が多い
- 状態変更を減らし、テストしやすくしたい
宣言型を選びやすい場合
- 求める結果や最終状態を明示できる
- 実行方法をDBやフレームワークへ任せたい
- 同じ構成を再現したい
どれを選ぶべきか
特定のパラダイムへ統一することより、複雑さの置き場所を意識することが重要です。
- 手続き型は、複雑さを処理順序として表す
- オブジェクト指向は、複雑さを責務と協調へ分ける
- 関数型は、複雑さを値の変換と関数の合成へ分ける
- 宣言型は、実行方法の複雑さを処理系へ任せる
短い処理へ多くのクラスを作る必要はなく、状態遷移の多い業務ロジックを一つの巨大な手続きへ書く必要もありません。問題に合う考え方を組み合わせます。
更新履歴
- 初版作成