どっちを学ぶべき?関数型 vs オブジェクト指向【新人エンジニア向け比較ガイド】

こんにちは。ゆうせいです。

これまでの記事で、副作用のないクリーンな「純粋関数」という考え方についてお話ししましたね。

実は、あの純粋関数は「関数型プログラミング」という、大きな設計思想の一部なんです。そして、プログラミングの世界にはもう一つ、非常に有名で広く使われている「オブジェクト指向プログラミング」という考え方が存在します。

新人エンジニアの皆さんにとっては、「一体どっちを学べばいいの?」「何がどう違うの?」と、少し混乱してしまうかもしれません。

今回は、この二大巨頭とも言える考え方が、それぞれどんな哲学を持っているのか、具体的なPythonコードを交えながら分かりやすく解説していきます!


2つのプログラミング哲学

まず大前提として、どちらが優れていて、どちらが劣っているという話ではありません。

これは、料理で言うところの「フレンチの哲学」と「日本料理の哲学」のようなもの。どちらも素晴らしい料理を作るための方法論ですが、素材へのアプローチや考え方が違うのです。

  • オブジェクト指向プログラミング (OOP):現実世界の「モノ」を主役にする考え方です。データ(状態)と、そのデータを操作する手続き(振る舞い)を一つの「オブジェクト」としてまとめ、モノ同士の相互作用としてプログラムを組み立てていきます。
  • 関数型プログラミング (FP):データの「変換」や「流れ」を主役にする考え方です。純粋関数を使い、あるデータをインプットとして受け取り、新しいデータをアウトプットとして返す、という処理の連鎖としてプログラムを組み立てていきます。

例え話:自動車工場のアプローチ

言葉だけだとイメージしづらいので、自動車を作る工場で例えてみましょう。

それぞれの考え方を、Pythonのコードで表現するとどうなるか、見比べてみてください。

オブジェクト指向(OOP)工場:役割分担された専門家チーム

OOPの工場では、「エンジン担当」「タイヤ担当」「塗装担当」といった、それぞれが独立した専門家「オブジェクト」がいるんでしたね。車というオブジェクトの状態を、専門家オブジェクトがメッセージを受け取って変更していきます。

これをPythonのコードで表現すると、こんな雰囲気になります。

データ(色やエンジンの状態)と振る舞い(paintメソッド)が Car というクラスにまとめられている点に注目してください。

# OOPのアプローチ例

class Car:
    # Carオブジェクトは自分自身の状態(色など)を知っている
    def __init__(self):
        self.color = None
        self.engine = None

    # 塗装という「振る舞い」を持つ
    def paint(self, color):
        print(f"{color}に塗装します")
        self.color = color

    # エンジン搭載という「振る舞い」を持つ
    def mount_engine(self, engine_type):
        print(f"{engine_type}を搭載します")
        self.engine = engine_type

# --- 車の製造 ---
# 1. まずは車オブジェクトを作る
my_car = Car()
print(f"製造開始時の車の状態: 色={my_car.color}, エンジン={my_car.engine}")

# 2. 車オブジェクトに「塗装して」というメッセージを送る
my_car.paint("赤")

# 3. 車オブジェクトに「エンジンを搭載して」とメッセージを送る
my_car.mount_engine("V8エンジン")

print(f"完成した車の状態: 色={my_car.color}, エンジン={my_car.engine}")

my_car というオブジェクト自身が、paint などのメソッド(振る舞い)によって状態を変化させていく様子がわかりますね。

関数型(FP)工場:巨大なベルトコンベアと専門機械

一方、FPの工場は、ベルトコンベアを流れるデータ(車の情報)を、各工程の機械(関数)が次々と変換していくイメージでした。

こちらの考え方をPythonで表現してみましょう。

車の状態はシンプルな辞書で表現し、それを変更するのではなく、新しい辞書を返す「純粋関数」を定義している点に注目してください。

# FPのアプローチ例

# 塗装を行う純粋関数
# 車の情報(辞書)と色を受け取り、「新しい」車の情報を返す
def paint(car_info, color):
    print(f"{color}に塗装します")
    # 元の辞書をコピーして変更(イミュータブルの思想)
    new_car_info = car_info.copy()
    new_car_info["color"] = color
    return new_car_info

# エンジンを搭載する純粋関数
def mount_engine(car_info, engine_type):
    print(f"{engine_type}を搭載します")
    new_car_info = car_info.copy()
    new_car_info["engine"] = engine_type
    return new_car_info

# --- 車の製造 ---
# 1. まずは元のデータ(車の初期情報)を用意
initial_car = {"color": None, "engine": None}
print(f"製造開始時の車の情報: {initial_car}")

# 2. 初期情報をpaint関数に通し、新しい情報を得る
car_after_paint = paint(initial_car, "赤")

# 3. 塗装済みの情報をmount_engine関数に通し、さらに新しい情報を得る
finished_car = mount_engine(car_after_paint, "V8エンジン")

print(f"完成した車の情報: {finished_car}")
print(f"元の車の情報は変わらない: {initial_car}")

データと処理(関数)が完全に分離されており、関数は副作用なく新しいデータを返すだけ。まるでベルトコンベアをデータが流れていくように見えませんか?


考え方の違いまとめ

この例え話から、両者の思想的な違いをまとめてみましょう。

観点オブジェクト指向 (OOP)関数型 (FP)
主役モノ(オブジェクト)データの変換(関数)
関心事責務の分割。誰が何をするか。データの流れ。どうデータが変わっていくか。
データ振る舞いとセットでカプセル化する振る舞い(関数)とは分離して考える
状態の扱いオブジェクトが自身の状態を管理・変更する状態の変更を避け、不変(イミュータブル)を好む

まとめ:どちらを学ぶべきか?

ここまで読んで、「なるほど、考え方が全然違うんだな」と感じていただけたでしょうか。

では、新人エンジニアの皆さんはどちらを学べばよいのでしょう?

答えは、「両方の基本的な考え方を学ぶべき」です。

なぜなら、現代の多くのプログラミング言語(Python, JavaScript, C#, Javaなど)は、どちらか一方に特化しているわけではなく、両方のパラダイムの良いところを取り入れた「マルチパラダイム言語」だからです。

実際の開発では、アプリケーションの全体的な骨格はオブジェクト指向で作り、データの複雑な計算や変換処理の部分は関数型の考え方を使ってクリーンに書く、といったハイブリッドなアプローチが非常に多く取られています。

  • まずはOOPから: 多くの現場で基本とされている考え方なので、クラスやインスタンスといったOOPの基本概念はしっかりと押さえましょう。「継承」や「ポリモーフィズム」といったキーワードも、いずれ学ぶことになります。
  • 次にFPのエッセンスを: その上で、純粋関数やイミュータブルなデータといったFPの考え方を学ぶと、コードの質を一段階引き上げることができます。

二つの哲学は対立するものではなく、あなたの引き出しを増やしてくれる強力な武器です。それぞれの良さを理解し、適材適所で使い分けられるエンジニアを目指していきましょう!

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。