0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

第3章|今さら学ぶ「オブジェクト指向」

0
Last updated at Posted at 2026-02-18

第3章|今さら学ぶ「オブジェクト指向」

📚 シリーズ目次はこちら → 「今さら学ぶ」シリーズ — はじめに
🗺️ KnowledgeNoteの設計を確認 → 設計マップ

この章でわかること

  • オブジェクト指向の本質は「変更に強いコードを作る」こと
  • クラスとインスタンスの関係 — 「設計図と実物」
  • 継承 — 共通の性質を親から受け継ぐ
  • ポリモーフィズム — 同じ命令で違う動きをさせる
  • カプセル化 — 触らせたくない部分を隠す
  • モジュールとミックスイン — 継承とは別の「機能の共有」
  • KnowledgeNoteのモデル設計で見る「オブジェクト指向の実践」

🏠 たとえ話で掴む「オブジェクト指向」

オブジェクト指向を一言でいうと、 「モノ(オブジェクト)を中心にプログラムを組み立てる考え方」 です。

これだけだとピンとこないので、 動物園 にたとえて考えてみる。

動物園には色々な動物がいる。犬、猫、鳥。それぞれ違う特徴を持っているが、共通点もある。

動物園のたとえ オブジェクト指向
「動物」という大きなくくり クラス (設計図)
実際の「ポチ」「タマ」「ピー太」 インスタンス (実物)
「動物はみんな名前と年齢を持つ」 属性(プロパティ)
「動物はみんな鳴ける」 メソッド
「犬は動物の一種」 継承
犬は「ワン!」、猫は「ニャー!」と鳴く ポリモーフィズム
動物の内臓の仕組みは外から触れない カプセル化

オブジェクト指向を学ぶと「きれいなコードが書ける」とよく言われるが、本当の価値は 「変更に強いコードが書ける」 ことです。

新しい動物(機能)を追加するとき、既存の動物(コード)を書き直さなくて済む。そういう設計を可能にするのがオブジェクト指向の力です。


🏗️ クラスとインスタンス — 設計図と実物

クラスは「設計図」

クラス は、オブジェクトの設計図。「ユーザーとはこういうものだ」という定義を書きます。

# 設計図(クラス)を定義する
class User
  # initializeは「実物を作るときに最初に実行される処理」
  def initialize(name, email)
    @name = name      # @がつくとインスタンス変数(この実物だけのデータ)
    @email = email
  end

  def profile
    "#{@name}#{@email})"
  end
end

インスタンスは「実物」

設計図(クラス)から作った実物が インスタンスnew で作ります。

# 設計図から実物を作る
user1 = User.new("田中", "tanaka@example.com")
user2 = User.new("鈴木", "suzuki@example.com")

puts user1.profile   # => "田中(tanaka@example.com)"
puts user2.profile   # => "鈴木(suzuki@example.com)"

同じ設計図(Userクラス)から作っても、user1user2 はそれぞれ別のデータを持つ別の実物。同じ設計図から「赤い車」と「青い車」という別の実物が作れるのと同じ構造です。

インスタンス変数とattr_accessor

@name のように @ から始まる変数は インスタンス変数。そのインスタンスだけが持つデータで、クラスの外から直接アクセスはできない。

外からアクセスさせたい場合は、 attr_accessor (アトリビュート・アクセサ)を使います。

class User
  attr_accessor :name, :email   # 読み書き両方OK
  # attr_reader :name           # 読み取りだけ(書き換え不可)
  # attr_writer :name           # 書き込みだけ(外から読めない)

  def initialize(name, email)
    @name = name
    @email = email
  end
end

user = User.new("田中", "tanaka@example.com")
puts user.name         # => "田中"(読み取り)
user.name = "山田"     # 書き換え
puts user.name         # => "山田"

💡 Railsの モデルUserArticle)では、attr_accessor を書かなくてもDBのカラム名で自動的にアクセスできる。これはActiveRecordが裏側で自動生成してくれているからです(→ 第15章で詳しく扱います)。


🧬 継承 — 共通の性質を親から受け継ぐ

継承とは

継承 は、あるクラスの機能を別のクラスが引き継ぐ仕組み。動物園のたとえでいうと、「犬は動物の一種だから、動物の特徴(名前を持つ、動ける)をそのまま受け継いでいる」ということです。

# 親クラス(スーパークラス)
class Animal
  def initialize(name)
    @name = name
  end

  def introduce
    "私は#{@name}です"
  end
end

# 子クラス(サブクラス)— Animalを継承
class Dog < Animal         # < で「〜を継承する」という意味
  def speak
    "ワン!"
  end
end

class Cat < Animal
  def speak
    "ニャー!"
  end
end

dog = Dog.new("ポチ")
puts dog.introduce   # => "私はポチです"(親のメソッドが使える)
puts dog.speak       # => "ワン!"(自分のメソッドも使える)

Dog クラスは introduce メソッドを自分で定義していないが、親の Animal から受け継いでいるので使える。 共通の処理を親クラスにまとめることで、コードの重複を減らせる のが継承の利点です。

Railsでの継承

Railsを書いていると、必ず見るのがこのパターン。

class ArticlesController < ApplicationController
  # ...
end

ArticlesControllerApplicationController継承 している。ApplicationController に定義した before_action やヘルパーメソッドが全コントローラで使えるのはこのおかげです。

同じように、モデルも ApplicationRecord を継承している。

class Article < ApplicationRecord
  # ApplicationRecordを継承 → ActiveRecordの機能が全て使える
  # .find, .where, .create などのメソッドは継承で手に入る
end

🎭 ポリモーフィズム — 同じ命令で違う動きをさせる

ポリモーフィズム(多態性) は、同じメソッド名で呼び出しても、オブジェクトの種類によって振る舞いが変わる仕組みです。

動物園で「鳴いて!」と言ったとき、犬は「ワン!」、猫は「ニャー!」、鳥は「ピヨ!」と鳴く。命令は同じ「鳴いて」だが、動物によって結果が違う。これがポリモーフィズムです。

class Dog < Animal
  def speak
    "ワン!"
  end
end

class Cat < Animal
  def speak
    "ニャー!"
  end
end

class Bird < Animal
  def speak
    "ピヨ!"
  end
end

# 同じ speak メソッドを呼ぶだけで、動物ごとに違う結果が出る
animals = [Dog.new("ポチ"), Cat.new("タマ"), Bird.new("ピー太")]

animals.each do |animal|
  puts "#{animal.introduce}#{animal.speak}"
end
# => 私はポチです:ワン!
# => 私はタマです:ニャー!
# => 私はピー太です:ピヨ!

なぜポリモーフィズムが嬉しいのか

ポイントは、 呼び出す側のコードを変えなくていい こと。

新しい動物(たとえば Frog クラス)を追加しても、animals.each のコードは一切変更不要。Frog クラスに speak メソッドを定義するだけで、自然と全体に組み込まれます。

# 新しいクラスを追加するだけ。既存コードの変更なし!
class Frog < Animal
  def speak
    "ケロケロ!"
  end
end

これが「変更に強い」設計。RailsではKnowledgeNoteの「いいね」機能など、 ポリモーフィック関連 として実際に使われています(→ 第18章で詳しく扱います)。


🔒 カプセル化 — ATMの裏側は触らせない

カプセル化 は、オブジェクトの内部の仕組みを外から隠すこと。

ATMで考えるとわかりやすい。利用者がやることは「カードを入れる」「暗証番号を入力する」「金額を指定する」だけ。ATMの中で紙幣をどうやって数えているか、通信をどうしているかは知らなくていいし、触れない。

プログラムでも同じ。 外から使う人が知る必要のない処理は隠す ことで、間違った使い方を防ぎます。

class BankAccount
  def initialize(owner, balance)
    @owner = owner
    @balance = balance   # 残高は外から直接いじれないようにする
  end

  # 外から使えるメソッド(公開インターフェース)
  def deposit(amount)
    if amount > 0
      @balance += amount
      "#{amount}円を入金しました。残高:#{@balance}円"
    else
      "入金額が不正です"
    end
  end

  def balance_info
    "#{@owner}さんの残高:#{@balance}円"
  end

  private   # ここから下は外から呼べない

  # 内部でだけ使うメソッド(利用者には見せない)
  def calculate_interest
    @balance * 0.001   # 利息計算(内部処理)
  end
end

account = BankAccount.new("田中", 10000)
puts account.deposit(5000)       # => "5000円を入金しました。残高:15000円"
puts account.balance_info        # => "田中さんの残高:15000円"
# account.calculate_interest     # => エラー!privateメソッドは外から呼べない

Rubyのアクセス制御

Rubyでは、メソッドの公開範囲を3段階で制御できる。

キーワード 意味 ATMのたとえ
public どこからでも呼べる(デフォルト) ATMのボタン。利用者が押せる
private そのクラスの中からだけ呼べる ATMの内部基板。利用者は触れない
protected そのクラスと子クラスから呼べる ATMのメンテナンス用パネル。係員だけ触れる

💡 privateprotected の使い分けは第4章で詳しく扱います。まずは「private = 外から隠す」が押さえられていれば大丈夫です。


🧩 モジュールとミックスイン — 継承とは別の「機能の共有」

なぜ継承だけでは足りないか

Rubyの継承は 単一継承。つまり、1つのクラスが持てる親クラスは1つだけ。「犬は動物でもありペットでもある」のように、複数の分類にまたがる機能を共有したい場合、継承だけでは対応できません。

ここで登場するのが モジュール(Module) です。

モジュールとは — 技術的な定義

モジュール は、メソッドや定数をまとめた入れ物。クラスとの最大の違いは、 インスタンスを作れない こと。モジュールは設計図ではなく、 機能のパーツ集 のようなもの。

クラスにモジュールを include すると、そのモジュールのメソッドがクラスに追加される。これを ミックスイン と呼びます。

# モジュール = 機能のパーツ集
module Taggable
  def add_tag(tag)
    @tags ||= []
    @tags << tag
  end

  def tags
    @tags || []
  end
end

# 記事にもユーザーにも「タグ機能」をつけたい
class Article < ApplicationRecord
  include Taggable   # ミックスイン
end

class User < ApplicationRecord
  include Taggable   # 同じモジュールを別のクラスにも
end

第2章で学んだ Enumerable も実はモジュール。ArrayやHashが include Enumerable しているから、selectmap が使えていたということです。

継承 vs ミックスインの使い分け

方法 使う場面 Railsでの例
継承(< 「AはBの一種」という関係 Article < ApplicationRecord
ミックスイン(include 「AにもBにも同じ機能を持たせたい」 include Enumerableinclude Taggable

🛠️ KnowledgeNoteでの具体例

KnowledgeNoteのモデルで、ここまでの概念が実際にどう使われるか。

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  # 全モデル共通の親クラス(継承の起点)
  self.abstract_class = true
end

# app/models/user.rb
class User < ApplicationRecord   # ApplicationRecordを継承
  has_many :articles, dependent: :destroy
  has_many :comments, dependent: :destroy
  has_many :likes, dependent: :destroy

  has_secure_password   # パスワード暗号化(カプセル化:内部の暗号化処理は隠蔽)

  validates :name, presence: true
  validates :email_address, presence: true, uniqueness: true

  # 公開メソッド — 外から呼んで使う
  def display_name
    "@#{name}"
  end

  def has_role?(role_name)
    roles.exists?(name: role_name.to_s)
  end

  private   # カプセル化:外からは呼ばせない

  # 内部でだけ使うヘルパーメソッド
  def normalize_email
    self.email_address = email_address.strip.downcase
  end
end
# app/models/article.rb
class Article < ApplicationRecord
  belongs_to :user
  has_many :comments, dependent: :destroy
  has_many :likes, as: :likeable, dependent: :destroy   # ポリモーフィズム!

  validates :title, presence: true
  validates :body, presence: true

  scope :published, -> { where(published: true) }

  def author_name
    user.display_name   # Userクラスのメソッドを呼ぶだけ
  end
end

has_many :likes, as: :likeable の部分に注目。記事(Article)にもコメント(Comment)にも「いいね」がつけられるが、呼び出し方は同じ likes。これがポリモーフィズムの実践です。設計が大きくなったときのパターン(Service Object等)については(→ 第25章で詳しく扱います)。

# ポリモーフィズムの実例 — 記事でもコメントでも同じように「いいね」できる
article = Article.find(1)
comment = Comment.find(1)

article.likes.count   # => 記事のいいね数
comment.likes.count   # => コメントのいいね数
# どちらも .likes で統一的にアクセスできる

📊 概念のまとめ — なぜオブジェクト指向が大事か

概念 何を解決するか KnowledgeNoteでの例
継承 コードの重複を減らす 全モデルが ApplicationRecord を継承
ポリモーフィズム 新しい種類を追加しやすくする 記事にもコメントにも同じ likes が使える
カプセル化 間違った使い方を防ぐ has_secure_password でパスワード処理を隠蔽
ミックスイン 継承なしで機能を共有する include Enumerable で配列処理メソッドを一括取得

共通する目的は 「変更に強いコードを作る」 こと。

機能を追加するときに既存コードを壊さない。担当が変わっても安全に使える。これがオブジェクト指向で設計する本当の理由です。


💼 面接で聞かれたら?

Q:オブジェクト指向の3つの特徴を説明してください。

「オブジェクト指向の3つの特徴は、継承・ポリモーフィズム・カプセル化です。継承は親クラスの機能を子クラスが引き継ぐ仕組みで、コードの重複を減らせます。ポリモーフィズムは同じメソッド名でもオブジェクトの種類によって振る舞いが変わる仕組みで、機能追加時に既存コードの変更が不要になります。カプセル化は内部の実装を隠して公開インターフェースだけを外に見せる仕組みで、安全に使える設計を実現します。」

深掘りされたら:

  • 「Railsでの継承の例は?」→ 全コントローラが ApplicationController を継承し、全モデルが ApplicationRecord を継承している。共通の処理を親に書けば全体に行き渡る。
  • 「ポリモーフィズムの実例は?」→ 「いいね」機能。記事にもコメントにもいいねがつけられるが、Likeモデルは1つで、likeable_type で対象を区別する(ポリモーフィック関連)。
  • 「モジュールとクラスの違いは?」→ クラスはインスタンスを作れるが、モジュールは作れない。モジュールは include でクラスに機能を追加する(ミックスイン)ための仕組み。Rubyは単一継承なので、複数の機能を共有したいときにモジュールを使う。

🔗 もっと深く知りたい人へ(1次情報リンク)


まとめ

  • ✅ オブジェクト指向の本質は「変更に強いコードを作る」こと
  • ✅ クラスは「設計図」、インスタンスは「実物」。new で設計図から実物を作る
  • ✅ 継承は親クラスの機能を子が受け継ぐ仕組み。Railsでは < ApplicationRecord が代表例
  • ✅ ポリモーフィズムは同じメソッド名で異なる振る舞いを実現する。機能追加時に既存コードを壊さない
  • ✅ カプセル化は private で内部処理を隠す。安全なインターフェースだけを外に公開する
  • ✅ モジュールは include でクラスに機能を追加する(ミックスイン)。Enumerableもモジュール

📚 シリーズ目次:「今さら学ぶ」シリーズ — はじめに

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?