Rubyで作るファーストクラスコレクション

# はじめに
　「現場で役立つシステム設計の原則　変更を楽で安全にするオブジェクト指向の実践技法」という書籍を読んでいるのですが、その中でファーストクラスコレクションという考え方を知りました。

　この書籍の中ではサンプルコードがJavaで書かれているのですが、私は普段Rubyを使っているため、Rubyでの実装方法について考えて見ようと思います。

# ファーストクラスコレクションとは
　配列をラップしたクラスのことで、対象の配列に対する処理を全て集約したクラスになります。別名コレクションオブジェクトとも呼ばれるそうです。

　配列周りは、以下のように複雑な処理が行われることが多いです。

   - for文などのループ処理
   - 配列やコレクションの要素の数が変換する（可能性がある）
   - 個々の要素の内容が変化する（可能性がある）
   - 0件の場合の処理
   - 要素の最大数の制限
  
　これらの処理を全てファーストクラスコレクションの中へ集約し、コードを整理します。


**サンプルコード**
　`stores`という配列に関する処理を、全て`Stores`クラスの中にまとめています。

```ruby
class Stores
  def initialize(stores)
    @stores = stores
  end

  def size
    return @stores.size
  end

  def show_all
    if @stores.empty?
       puts '店舗が存在しません。'
       return
    end

    @stores.each do |store|
      puts store.name
    end
  end
end
```


# メリット
　配列の複雑さを専用の小さなクラスに閉じ込めることで、対象の配列に関する処理がプログラムの至る所に散らばらず、整理することができます。

その結果

   - **メンテナンス性が向上する**
   - **プログラムが分かりやすくなる**

などの効果が得られます。

# 注意点
　コンストラクタで与えた配列のオブジェクトに対して、**Setterを作らないようにする方が望ましいです**。要素の追加や削除が外部から行えるようになってしまうため、ラップした意味がなくなってしまいます。


# 実装方法
　それでは早速実装していきたいと思います。

### その１：　Arrayを継承させたクラスを作る

　まず単純にArrayクラスを継承させた方法を考えてみます。

```ruby
class Stores < Array
  def output_store
    self.each { |store|
      puts "店名： #{store}"
    }
  end
end

stores = Stores.new(['A店', 'B店', 'C店'])
puts stores.size
# => 3
stores.output_store
# => 店名: A店
# => 店名: B店
# => 店名: C店
```

　これで、固有のビジネスロジックを持った上で、Arrayが備える機能も保持したクラスを作ることができました。

- **この実装の問題点**

　しかし、このクラスには以下の様な問題点もあります。

```ruby
stores1 = Stores.new(['A店', 'B店', 'C店'])
stores2 = Stores.new(['D店', 'E店', 'F店'])
puts stores1.class
=> Stores
puts stores2.class
=> Stores

stores3 = stores1 + stores2
puts stores3.class
=> Array
# => 3

stores3.output_store()
# => NoMethodError
```

　Storesのオブジェクト同士を+で連結することによって、元のArrayクラスに戻ってしまいます。
　Arrayクラス自体機能が多いため、全ての機能を継承させることで、不要な機能まで搭載してしまう不利益の方が多いという印象です。

### その２： Arrayから必要なメソッドのみラップする
　今度はArrayから必要なメソッドだけラップする形を考えます。

```ruby
class Stores
  def initialize(stores)
    @stores = stores
  end
  
  def size
    @stores.size
  end

  def output_store
    @stores.each { |store|
      puts "店名： #{store}"
    }
  end
end

stores = Stores.new(['A店', 'B店', 'C店'])
puts stores.size
# => 3
stores.output_store
# => 店名： A店
# => 店名： B店
# => 店名： C店
```

　これで対象のクラスにとって必要な機能のみを搭載した、最小構成クラスを作ることができました。

- **このクラスの問題点**

　このパターンの場合だと、Arrayクラスからラップしたいメソッドが増えるたびに、新たなメソッドを定義することになります。

```ruby
class Stores
  def initialize(stores)
    @stores = stores
  end
  
  def size
    @stores.size
  end
  
  def each
    @stores.each { |store| yield(store) }
  end
  
  def select
    @stores.select{ |store| yield(store) }
  end

  def index(obj)
    @stores.index(obj)
  end

  .
  .
  .

end
```

ラップ用のクラスを都度定義するのは面倒ですし、コードの量も増えて見にくくなりそうです。


### その3: 委譲を使用したやり方
　そこで今度は委譲という考え方を使ってみます。Rubyの場合、`Forwardable`モジュールを使うことで実現できるみたいです。

```ruby
require 'forwardable'
class Stores
  extend Forwardable
  def_delegators :@stores, :size

  def initialize(stores)
    @stores = stores
  end

  def output_store
    @stores.each { |store|
      puts "店名： #{store}"
    }
  end
end

stores = Stores.new(['A店', 'B店', 'C店'])
puts stores.size
# => 3
stores.output_store
# => 店名： A店
# => 店名： B店
# => 店名： C店
```

これでコードが大分スッキリしましたね。


# 実装部分で参考にした書籍
　「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」の第8章を参考に書いてみました。

　オブジェクト指向関連の書籍で、Rubyを題材にしてくれているものは少ない気がするので、Rubyでプログラミングを書きはじめて、オブジェクト指向を学びたい方にはぜひオススメしたいです。