Effective Ruby の読書メモ。あまり良いメモではない。
1章 Ruby に身体を慣らす
Ruby は何を真と考えているかを正確に理解しよう
Ruby での boolean は 偽 は false と nil であり、それ以外は真である。
true, false は ruby ではグローバル変数である。
そしてその値は TrueClass, FalseClass である。
nil と false を区別したいときは nil? メソッドを使う。
x == false を計算すると、オブジェクト x の実装によっては false でも nil でもないオブジェクトが真を返すことがありえる。
オブジェクトを扱うときには nil かもしれないということを忘れないようにしよう
すべてのオブジェクトは BaseObject を継承する。
型よりもインターフェース(どんなメソッドを持っているか)を重視する。
メソッドを持ってないなら NoMethodError が発生する。
予想外の状況で nil が渡ってきてエラーになる事が多い。
tos は nil に対応している。
Array#compact なども便利だ。
Ruby の暗号めいた Perl 風の機能を避けよう
=~ のような記号はやめて match を使おう。
定数がミュータブルなことに注意しよう
先頭が大文字になっている識別子は全て定数である。
String, Array も例外ではなく、クラスが代入された定数。
freeze で更新を抑止しないと定数は上書きされる危険がある。
警告は発生するが定数は再代入できる。
モジュールにも freeze は使える。
実行時の警告に注意しよう
プログラムを実行しようとする直前に、インタプリタはコードを解釈し中間言語にコンパイルする。
コンパイル時に警告を出すことが有る。
ruby は曖昧なコードも解釈できる File.join *args とか。
警告を出すにはオプションが必要なことも有る。
多くの場合カッコを書けばいい。
RUBYOPT 環境変数を使って警告表示することもできる。
2章 クラス、オブジェクト、モジュール
Ruby が継承階層をどのように組み立てるかを頭に入れよう
オブジェクトはクラスのインスタンスである。インスタンス変数を持つことができる。
クラスもまたオブジェクトである。クラス変数を持つことができる。
スーパークラスは親クラスのこと。
モジュールは Module クラスのインスタンスである。そして new メソッドがない。
特異クラスは、継承階層に含まれている名前のない不可視のクラスを指すわかりにくい用語
名前がない
インスタンスを作ることはできない
include でモジュールをミックスインしたときに起こること
特異クラスを作成してクラス階層に挿入している
なのでメソッドの探索順は決まっている(最後にミックスインしたものから優先して探索される)
なのでミックスインしたモジュールが元のクラスのメソッドをオーバーライドすることは不可能
特異メソッド
特定のインスタンス x にだけにメソッドを生やすことができる。この機能を特異メソッドと呼ぶ。
x に特異メソッドを追加するときは
無名クラス y を作る
y にインスタンスメソッドを定義
y を x の特異クラスに差し込む
super のふるまいが一通りでないことに注意しよう
super はふつう、スーパークラスのメソッドを呼び出す。
これはメソッドのように見えるが実際はキーワードである。
カッコを省略しない場合は引数を明示的に渡す。
カッコを省略した場合は引数を暗黙的に渡す。(呼び出し元のメソッドの引数を全て渡す)
引数の個数が違う場合はカッコを省略できない。
モジュールで定義されてるメソッドもメソッドルックアップの対象になり super で呼び出せる。
しかし、スーパークラスにも、モジュールにも定義されているメソッドがある場合は、 super で呼び出されるのはもっとも継承階層が近いものになるので注意が必要。
super がメソッドルックアップに失敗したら methodmissing を呼び出す。
もしも methodmissing のデフォルトの振る舞いを書き換えていたら、super に失敗したことが見えなくなってしまうかもしれない。
methodmissing の中で super を呼び出すのは更に混乱する
なので methodmissing は定義しないほうがよい
サブクラスを初期化するときは super を呼び出そう
inilialize は単純な private メソッドで、普通のメソッドルックアップと同じように動く。
明示的に super を呼ばないと、親クラスの initialize は実行されない。
(これは他の言語のコンストラクタとは異なるので注意が必要だ)
initializecopy を定義すれば、dup や clone の振る舞いを変えられる。
9. Ruby の最悪に紛らわしい構文に注意しよう
メソッド名の末尾には非英数字を使える。
とくに末尾が = になっているのはセッターメソッド。
attrwriter, attraccessor などを使って定義する事が多い。
単なる変数の代入と、アクセサの呼び出しが混同されやすいので注意。
10. 構造化データの表現には Hash でなく Struct を使おう
Hash は便利だがオブジェクト指向らしくやるなら Struct のほうが良い
struct ならそれ自体にメソッドをもたせられる
struct なら属性名をタイプミスしたときにエラーを出せる
11. モジュールにコードをネストして名前空間を作ろう
名前空間がないと、クラス名の衝突が起こりうる
ネストしないスタイルでの宣言は事前に名前空間を定義しておく必要があるので注意
ruby にはグローバルな名前空間というものがなく Object クラスに格納されている
すべてのクラスは Object を継承しているので、その階層を通じて発見される
12. さまざまな等値の違いを理解しよう
==
とequal?
とeql?
と===
は違う==
は緩やかな比較オーバーライドしてもよい
この結果はクラスに委ねられている
何も実装してない場合は equal? と同じ結果になる
equal?
は objectid の一致を調べる仕様なので振る舞いを変えるべきでないeql?
はハッシュキーが同じものかどうか判断するとき使われる===
は case 等値演算子デフォルトでは内部で == を呼び出すだけ
Regexp はオーバーライドしており文字列のマッチを行う
13. <=> と comparable モジュールで比較を実装しよう
<=> を実装すれば順序関係を定義できる
実は Object が <=> を実装しているがほとんど意味がない実装になっている
オブジェクト等しいかどうかだけチェックするようになっているらしい
オブジェクトが等しくないときは nil を返す
<=> が nil を返した場合、比較不能扱いなので sort は実行できない
<=> が満たすべき仕様
a < b なら -1 を返す
a > b なら 1 を返す
a == b なら 0 を返す
<=> を実装し comparable モジュールをミックスインすれば <, >, == などが定義される
14. protected メソッドを使ってプライベートな状態を共有しよう
private で定義されたメソッドは、レシーバをつけて呼び出すことができない
protected で定義されたメソッドは、レシーバは継承関係をもっていないといけない
例: class Widget に protected な overlapping? メソッドがあるとしたら
Widget 自身や、継承したクラスの中で overlapping を呼び出せる
15. クラス変数よりもインスタンス変数を使うようにしよう
クラス変数は @@ で始まる変数
シングルトンパターンを実装するときにクラス変数が使われる事がある
ある種のグローバル変数みたいなもの
クラス変数は、それを継承したクラスとも共有されてしまう
クラスインスタンス変数は、それを継承したクラスと共有されないので、こちらを使うと安全
クラス変数やクラスインスタンス変数はスレッドセーフでない
並列処理をするならミューテックスを使ってセットするべき
3章 コレクション
16. コレクションを書き換える前に引数として渡すコレクションのコピーを作っておこう
コレクションの要素は Integer 以外は参照渡しになる
コレクションをコピーしておけば、破壊的な変更をしたときにオリジナルに影響が及ばない
コピーには clone と dup がある
clone はフリーズ状態も維持する。そして特異メソッドも維持する。
dup は上記ふたつを維持しない。
殆どの場合は dup で良い。
dup の振る舞いを変えたいときは initializecopy をオーバーライドしよう。
簡単な実装としては Marshal でダンプしてロードしたらディープコピーになる事が多い。
ただ、メモリを無駄に消費するし、遅い処理になるので注意。
17. nil やスカラーオブジェクトを配列にするには Array メソッドを使おう
実は Kernel.Array というメソッドがある
(ActiveSuppot で追加される Array.wrap とよく似ているが Hash を展開するのが違う)
18. 要素が含まれているかどうかの処理を効率よく行うために集合を使うことを検討しよう
Array, Hash, Range はコアライブラリで、前準備なしに利用できる
Set はコアライブラリではないコレクションクラス
Set は要素を含んでいるかどうかのチェックが速い
Hash は重複キーを持てないことに注意
Set は内部的にHashを作っている
Set は順序を持たないことに注意
順序が必要なら SortedSet を使う -> gem に追い出された