最終更新:
【図解で比較】オブジェクト指向 vs 関数型プログラミング — 2大パラダイムの違いを徹底解説
オブジェクト指向と関数型プログラミングってよく聞くけど、何がどう違うの?
たとえば「動物」というクラスを作って、その中に「名前」や「鳴き声」というデータと、「鳴く」という操作をまとめる。そこから「犬」や「猫」を継承で派生させれば、共通部分は再利用しつつ個別の振る舞いだけ上書きできる。内部の操作を決めた入口にまとめるカプセル化も大切だね。継承は設計の選択肢の1つで、すべてのオブジェクト指向設計で必要なわけではないよ。
じゃあ関数型のほうはどういう考え方なの?
状態の管理の仕方がだいぶ違いそうだね?
それぞれの代表的な言語って何があるの?
Reactのフックも関数型の影響を受けてるって聞いたことがあるよ!
結局どっちを使えばいいの?
純粋関数かどうかは、どう見分ければいい?
たとえば受け取った数を2倍して返すだけなら純粋な計算だよ。同じ値を返しても、毎回ファイルへ書き込むなら副作用があるので純粋とはいえない。計算と保存処理を分けると、計算部分はファイルを用意せずテストできる。こうした小さな分離から、両方の考え方を組み合わせられるんだ。
同じ「買い物かご」を2通りで書く
JavaScriptで300円の商品を2個追加する例です。違う問題で比べるより、同じ結果をどのように表すかを見ると理解しやすくなります。
オブジェクトにデータと操作をまとめる
class Cart {
#items = [];
add(price, quantity) {
this.#items.push(
{ price, quantity }
);
}
total() {
return this.#items.reduce(
(sum, item) =>
sum + item.price *
item.quantity,
0
);
}
}
const cart = new Cart();
cart.add(300, 2);
console.log(cart.total()); // 600
商品を持つcartに追加・合計の操作を依頼します。この例では内部配列を直接触らず、addを通して状態を変えます。入力値の検証などは省略しており、そのまま販売システムに使うコードではありません。
関数で新しい値へ変換する
const addItem = (
items, price, quantity
) => [
...items,
{ price, quantity }
];
const total = items => items.reduce(
(sum, item) =>
sum + item.price * item.quantity,
0
);
const before = [];
const after = addItem(
before, 300, 2
);
console.log(total(after)); // 600
// 元の配列は変更しない
console.log(before.length); // 0
addItemは受け取った配列を変更せず、新しい配列を返します。ただしJavaScriptの配列自体が不変になるわけではなく、他のコードが変更すれば結果も変わります。表示や保存のためのconsole.logなどは副作用なので、純粋な計算部分とは分けて考えます。
選び方のまとめ
オブジェクトの操作を入口として整理し、その内部の合計計算を純粋関数へ分ける、といった組み合わせもできます。オブジェクト指向なら変更可能、関数型なら副作用が一切ない、という二択ではありません。役割と状態の置き場所を明確にするための考え方として使いましょう。