【初学者】オブジェクト指向とは？

オブジェクト指向について理解が浅かったので、記事にすることで少しでも理解を深めようという目的で書きました。

## オブジェクト指向とは？
オブジェクト指向とは、「変更に対して柔軟に対応するための開発手法」です。
各オブジェクトに責務を任せることでコードが理解しやすく、変更や機能追加もしやすくなります。

オブジェクト指向を理解するために重要な要素が3つあります。「継承」「ポリモーフィズム」「カプセル化」です。
それぞれについて解説していきます。

## 継承
継承とは、共通の振る舞いを抽象化してまとめることです。
継承することで、親クラスの変数やメソッドが子クラスへ引き継がれます。
これによって、複数のクラス間で同じ機能を再利用しやすくし、コードの重複を減らすことでメンテナンスも簡単にすることができます。
```ruby
class Product
  def initialize(name, price)
    @name = name
    @price = price
  end

  def show_info
    "商品名: #{@name}, 価格: #{@price}円"
  end
end

class Book < Product
  def initialize(name, price, author)
    super(name, price)
    @author = author
  end

  def show_info
    "#{super} 著者: #{@author}"
  end
end

product = Product.new("商品", 1000)
puts product.show_info
#=> 商品名: 商品, 価格: 1000円

book = Book.new("Ruby入門", 2800, "山田太郎")
puts book.show_info
#=> 商品名: Ruby入門, 価格: 2800円 著者: 山田太郎 
```
上記のコードでは、「商品」という親クラスを作り、その子クラスとして「本」を定義しています。共通する特徴（商品名、価格）は親クラスに定義し、本特有の情報（著者）は子クラスに定義しています。

```ruby
  def show_info
    "#{super} 著者: #{@author}"
  end
  ```
子クラスでは、親クラスと同名のメソッドを定義することで、親クラスの処理を上書きすることができます。
`Book`クラスの`show_info`メソッドでは、`super`を使って親クラスのメソッドを呼び出した上で著者情報も含めた文字列を返しています。
これは`Product`を親クラスとして継承していることで実現しています。

## ポリモーフィズム
呼び出すメソッドが同じでも、インスタンスによって違う振る舞いをすることをポリモーフィズムと言います。
```ruby
class Product
  def initialize(name, price)
    @name = name
    @price = price
  end

  def show_info
    "商品名: #{@name}, 価格: #{@price}円"
  end
end

class Book < Product
  def initialize(name, price, author)
    super(name, price)
    @author = author
  end

  def show_info
    "#{super} 著者: #{@author}"
  end
end

class DVD < Product
  def initialize(name, price, director)
    super(name, price)
    @director = director
  end

  def show_info
    "#{super} 監督: #{@director}"
  end
end

# 異なる種類の商品を作成
book = Book.new("Ruby入門", 2000, "田中 太郎")
dvd = DVD.new("映画A", 3000, "佐藤 監督")

# 異なる子クラスのインスタンスを同じ配列に格納
products = [book, dvd]

# 同じメソッド（show_info）を呼び出すことで、それぞれのインスタンスに応じた振る舞いが実行される
products.each do |product|
  puts product.show_info
end

# =>商品名: Ruby入門, 価格: 2000円 著者: 田中 太郎
# =>商品名: 映画A, 価格: 3000円 監督: 佐藤 監督
```
`product`は具体的なクラス（BookなのかDVDなのか）を意識せずに、同じメソッド（`show_info`）で異なるクラスのオブジェクトを扱えています。

ポリモーフィズムを活用すると、コードがきれいにまとまり、変更にも強くなります。

## カプセル化
オブジェクトのデータや機能を隠蔽し、外部から直接アクセスができないようにすることをカプセル化と言います。これにより、データの不正な変更を防ぎ、オブジェクトの内部状態を安全に保つことができます。

```ruby
class Product
  # インスタンス変数を読み取り専用にする
  attr_reader :name, :price

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

  def show_info
    "商品名: #{@name}, 価格: #{@price}円"
  end
end

class Book < Product
  # インスタンス変数を読み取り専用にする
  attr_reader :author

  def initialize(name, price, author)
    super(name, price)
    @author = author
  end

  def show_info
    "#{super} 著者: #{@author}"
  end
end

product1 = Product.new("ノート", 300)
book1 = Book.new("Ruby入門", 2000, "田中 太郎")

puts product1.show_info  # => 商品名: ノート, 価格: 300円
puts book1.show_info      # => 商品名: Ruby入門, 価格: 2000円 著者: 田中 太郎

# エラー例: インスタンス変数への直接代入
product1.name = "新しいノート"   #=> undefined method `name=' for an instance of Product (NoMethodError)
book1.price = 1800              #=> undefined method `price=' for an instance of Book (NoMethodError)
book1.author = "新しい著者"      #=> undefined method `author=' for an instance of Book (NoMethodError)
```
`attr_reader`を使ってインスタンス変数を読み取り専用にしているため、直接変更しようとするとエラーになります。
値を変更したい場合は、以下のように専用のメソッドを定義して行います。
```ruby
class Book < Product
  attr_reader :author

  def initialize(name, price, author)
    super(name, price)
    @author = author
  end

  def show_info
    "#{super} 著者: #{@author}"
  end
  
  # Bookクラスに著者を変更するメソッドを追加
  def update_author(new_author)
    @author = new_author
  end
end

book1.update_author("新しい著者")

# 変更後のデータ表示
puts book1.show_info      # => 商品名: Ruby入門, 価格: 2000円 著者: 新しい著者
```

##
今回理解を深めるために記事を書きましたが、オブジェクト指向についてまだまだ理解が足りないと感じています。
これからもより理解を深めていけるように頑張ります！

### 参考

https://youtu.be/7_hNvwSYEmo?si=LdzyidbiyJ67rcnD