ひとりTDDBC（LRUCache）仕様変更その1

# 前回までのあらすじ

- ひとりTDDBCをやります
- お題は[t_wadaさんのTDDBCお題スライド P.5](http://www.slideshare.net/t_wada/tddbc-exercise)
- Githubの[このリポジトリ](https://github.com/haazime/tddbc_lru_cache)で進めます
- [Vol.1](http://qiita.com/haazime/items/2c61cb10eea3c6d97e4d)

# 仕様変更その1

キャッシュのサイズを後から変更したいそうです。

```rb
cache = LRUCache.new(5)
cache.resize(2)
```

こんな感じで```resize```メソッドに変更したいサイズを渡して
キャッシュのサイズを変更できるようにしたいと思います。

# レビュー

[仕様変更その1に対応したコード](https://github.com/haazime/tddbc_lru_cache/tree/feature/resize)

## テスト

### RSpec.configureの```profile_examples```を無効にする

今回使っているRSpecのバージョンは3.1なのですが、
```rspec --init```で生成された```spec_helper.rb```の
```=begin``` ```=end```をアンコメントアウトして実行すると

```bash
[tddbc_lru_cache (feature/resize)]$ bundle exec rspec
Run options: include {:focus=>true}

All examples were filtered out; ignoring {:focus=>true}

...中略...

Finished in 0.00388 seconds (files took 0.15989 seconds to load)
10 examples, 0 failures

Top 10 slowest examples (0.00244 seconds, 62.9% of total time):
  LRUCache サイズを固定 :bが消える
    0.00115 seconds ./spec/lru_cache_spec.rb:34

　...中略...

  LRUCache サイズを固定 何も消えない
    0.00008 seconds ./spec/lru_cache_spec.rb:4

Top 3 slowest example groups:
　...中略...
    0.00015 seconds average (0.00046 seconds / 3 examples) ./spec/lru_cache_spec.rb:58

Randomized with seed 32109
```

こんな感じでテストをプロファイリングしてくれます。

テストスイートが大きくなり、最適化が必要になった時には便利ですが
今はまだ必要ないので、```spec_helper.rb```の

```rb
#config.profile_examples = 10
```

この部分をコメントアウトしてプロファイリングを無効にしました。

### 仕様変更部分のテスト

```rb
RSpec.describe LRUCache, 'サイズを5から2に変更' do
  let(:cache) { LRUCache.new(5) }

  before do
    cache[:a] = 'alpha'
    cache[:b] = 'bravo'
    cache[:c] = 'charlie'
    cache[:d] = 'delta'
    cache[:e] = 'echo'
  end

  it ':a,:b,:cが消える' do
    cache.resize(2)
    expect(cache.to_hash).to match(
      d: 'delta',
      e: 'echo',
    )
  end

  it ':a,:b,:dが消える' do
    cache[:a]
    cache[:c]
    cache[:e]
    cache.resize(2)
    expect(cache.to_hash).to match(
      c: 'charlie',
      e: 'echo'
    )
  end

  it ':a,:b,:d,:cが消える' do
    cache[:a]
    cache[:c]
    cache[:e]
    cache.resize(2)
    cache[:f] = 'foxtrot'
    expect(cache.to_hash).to match(
      e: 'echo',
      f: 'foxtrot'
    )
  end
end
```

- 何もせずにリサイズ
- リサイズ前にデータを取り出してからリサイズ
- リサイズ後にデータを入れる

この3つのテストケースで、リサイズしても正しくデータが消えているか（残っているか）をチェックします。

## プロダクションコード

```rb
   def initialize(capacity)
     @capacity = capacity
     @container = {}
-    @stored_key_order = []
+    @contained_key_order = []
   end
```

> @stored_key_order という名前は、データを入れた時のキーの順序という意図でしたが、
実際の役割は 
次にキャッシュサイズを超えた時に消されるキーの優先順位
になっているので、それにふさわしい名前に変えたほうがよさそうです。

前回、こう書きましたが実装を進めるうちに```@stored_key_order```は
役割が元に戻り```@contianed_key_order```に変わりました。

```rb
def resize(new_capacity)
  delta = @capacity - new_capacity
  @capacity = new_capacity
  return if delta < 0
  shape_container(reduce_contained_keys(delta))
end
```

今回の仕様変更で追加した```resize```メソッドはこんな感じになりました。

- ```@capacity```を新しいサイズにする
- 新しいサイズの方が小さければ```@container```をそのサイズに合わせる

#### @contained_key_orderふたたび

```[]``` 、```[]=``` 、```resize``` 、```reduce_contained_keys```の各メソッドで

- 要素の削除
- 要素の追加
- 要素の入れ替え
- 配列のサイズ変更

を直接```Array```のメソッドを使ってやっているのが気になります。

# つづく

[ひとりTDDBC（LRUCache）リファクタリング](http://qiita.com/haazime/items/85d52a3755f7ebca0a65)