チェックポイント
- ArrayはIteratorを使っているか?
- HashはIteratorを使っているか?
- 自分でIteratable(Enumerable)なクラスは書けるか?
- Rubyでインターフェースは存在しないがどう置き換えられているか?
#
#
#
#
#
#
#
#
#
#
#
#
# 自分で調べる時間確保のための余白
#
#
#
#
#
#
#
#
#
#
#
#
1. どういう時に使うか
集合の要素を全走査したいとき。
Rubyで言えば XXX.each
でループを回せる部分。
2. メリット (+デメリット)
メリット
個々の要素とその集合という概念を扱えるようになる。
デメリット
特になし。
3. このパターンを使わないとどうなるか
配列やDB的なidがあるものに関してはfor (int i = 0; i < x.size(); i++)
というような決まり文句で代替が効く。
文字列をKeyにした集合だと、そのkeyの配列などがない限り個々の要素にアクセスする方法がなくなってしまう。
4. Rubyではどう書くか
この本のAggregatorは実際のJavaではIterableになっている。Iteratorはそのまま。
これに対応するのが、EnumerableとEnumerator。
要素のクラスにEnumerableをincludeした上で、each
をyield
を用いて実装する。
def each
# ...
yield
# ...
end
ちなみに、Enumerableは、更に、要素に対して順序付けをすることも出来るようになっている。
その場合は<=>
という比較演算子を定義すれば良い。
これでsort
ができるので小さい順に操作したり、min
やmax
を取得することが出来る。
5. Ruby/Rails/Gemで実際に使われている場面
率直には、先ほどのEnumerable。
その他パターンを使っているのは、String, Array, Hash, RailsだとActiveRecordなど多岐にわたる。
その他余談
Rubyはインターフェースは存在しないがどう置き換えられているか
- 機能追加:モジュールとしてインクルードする(mixin)
これによりインターフェースの問題である実装を共有できなくコードの重複が生じる問題が解決できる。
- メソッドの実装の強制:テストを書くことで保証する
メソッドがあることだけ保証したくて実装はそのクラスによる場合は上のモジュールでは解決しない。ただそのメソッドが呼び出し可能であることを保証したいなら、TDDならテストを書けと言うのは、新しい考え方だけど納得できる。
ArrayListの実装、LinkedListとの違い
ArrayListってListを巡るようにイテレートしてるの??という話を聞かれたので、LinkedListとArrayListの違いを説明した。
ArrayListの実装は、ポインタで要素ごとを結ぶLinkedListとは違い、基本的にただの配列。予め、ある程度の大きさの配列を用意しておき、そこに要素を追加していく。予め用意された配列の大きさを超えるときはより大きい配列を作成しそこに今までの要素をコピーしてから追加する。
この時、サイズ増加を10,20,30,40というようにするとコピーのコストが無視できなくなるが、10,20,40,80というように指数的に増やしていけば、コピーのコストはO(1)になる。この違いは例えば1000個要素を追加するとき何回コピーが発生するかを考えればわかるはず。
この実装はオーバーヘッドがそんなにないためRubyの場合は配列といった時に既にこういう実装になっている。