銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

【技術書まとめ22】Effective Ruby

毎週1冊技術書を読んでブログでアウトプットすることが目標で今回が第22弾です。

今回は、Effective Ruby  を読みました。

Rubyの効果的なコードの書き方について詳細に解説している本です。

知らなかった書き方も多く、明日から即実務で使えるとても有意義な技術書でした。

Effective Ruby

Effective Ruby

 

f:id:ysk_pro:20190325192504p:plain

第1章 Rubyに身体を慣らす

  • Rubyでは、falseとnilを除くすべての値が真である。多くの言語とは異なり、Rubyでは数値ゼロは真となる
  • falseとnilを区別しなければならないときは、nil?メソッドを使うか、falseを左被演算子とする”==”演算子を使う
  • すべての変数がnilかもしれないという前提でコードを書くようにする。適切であれば、to_s、to_iなどの変換メソッドを使ってnilオブジェクトを強制的に型変換する
  • 定数とは、先頭が大文字になっている識別子のことである。クラスやモジュールの名前も実際には定数である
  • 定数は書き換えられないようにfreezeすべき。定数が配列やハッシュなどのコレクションオブジェクトは、コレクションとその要素それぞれをfreezeする
  • コンパイル時に生成される警告は重要である。大多数は、Rubyが曖昧な構文を検出し、さまざまな解釈のなかから1つを選んで先に進んだときに生成される。Rubyの将来のバージョンが解釈方法を変え、プログラムの動作が突然変わってしまうことを避けるために、曖昧さは取り除いておくべき
 

第2章 クラス、オブジェクト、モジュール

  • クラスは、メソッドと定数の入れ物である。メソッドはインスタンスメソッドと呼ばれ、そのクラスのインスタンスとなっているすべてのオブジェクトの振る舞いを表現する。しかし、クラスはそれ自体もオブジェクトなので、クラス変数という変数の入れ物でもあり、クラスメソッドを持っている
  • Rubyは多重継承をサポートしていないが、includeメソッドでクラスにモジュールをミックスインすれば、多重継承と同じような効果が得られる
  • Includeメソッドを使ってモジュールをミックスインした時、Rubyは水面下で特異クラスを作ってインクルードしたクラスの上の階層に挿入している。この無名で見えないクラスがモジュールにリンクされ、両者はインスタンスメソッドと定数を共有する
  • Rubyは、メソッドを探すときに、最後にインクルードされたモジュールから順にたどっていく。最後の階層まで探しているメソッドが見つからなかったときには、method_missingを探してもう一周サーチを行う
  • 継承階層の上位にあるメソッドをオーバーライドするときは、メソッド内でsuperを使ってオーバーライドされるメソッドを呼び出すことができる。引数もかっこも付けずにsuperを呼び出すと、呼び出し元のメソッドに渡されたすべての引数を渡してオーバーライドされるメソッドを呼び出すという意味になる。オーバーライドされるメソッドに引数を渡さずにsuperを使いたい場合は、super()のように空のかっこを使う必要がある
  • Rubyは、サブクラスのオブジェクトを作るときに、スーパークラスのinitializeメソッドは自動的に呼び出さない。initializeにも通常のメソッドルックアップの規則が適用され、最初に見つかったバージョンが実行される。スーパークラスのinitializeメソッドを使いたいときはsuperを用いる
  • セッターメソッドは、明示的にレシーバを指定しなければ呼び出せない。レシーバがなければ、変数への代入と解釈されてしまう。インスタンスメソッドからセッターメソッドを呼び出すときには、レシーバとしてselfを使う
  • 新しいクラスを作るほどでもない構造化データを扱うときには、HashではなくStructを使うようにすべき。Struct::newの戻り値を定数に代入し、その定数をクラスのように扱う
  • トップレベル定数を修飾なしで使うと曖昧になるときには、”::”を使ってフルに修飾する(例 ::Array)
  • equal?メソッドは、厳格にオブジェクトを比較し、両方が同じobject_idを持たない限りtrueを返さない
  • 2つのオブジェクトが同じ値を表すかどうかをテストするときには”==“演算子を使う
  • case式は、個々のwhen節をテストするために”===“演算子を使っている。左被演算子はwhenに与えられる引数、右被演算子はcaseに与えられる引数である
  • オブジェクトの順序は、”<=>”演算子を定義し、Comparableモジュールをインクルードすれば実装できる。”<=>”演算子は、左被演算子が右被演算子と比較できないものはnilを返す。左被演算子の方が小さいときは-1、大きいときは1、等しいときは0を返す
  • Rubyがprivateメソッドに加えている制限は1つだけで、privateメソッドは明示的なレシーバを指定して呼び出すことができないことである
  • protectedメソッドは、関連するクラスの間でプライベートな情報を共有するために作られる。レシーバを明示してprotectedメソッドを呼び出せるのは、同じクラスのオブジェクトか共通のスーパークラスからprotectedメソッドを継承しているオブジェクトだけである
 

第3章 コレクション

  • Rubyのメソッド引数は値渡しではなく参照渡しである。ただし、この規則には、Fixnumオブジェクトという例外がある
  • 引数として渡されたコレクション(配列やハッシュ)は、書き換える前にdup、cloneメソッドでコピーをつくるべきである(ほとんどの場合dupの方が良い)。メソッドに引数として渡されたコレクションを書き換えてしまう誤りはよくある
  • reduce(inject)は、関数型プログラミングの世界で畳み込みと呼ばれる関数で、あるデータを別のデータ構造に変換する、非常に強力な関数である
  • 以下例の動作は、イテレーションのたびに、ブロックは現在のアキュムレータと現在の要素を受け取り、それらを加える。この和はブロックの戻り値になり、次のイテレーションのアキュムレータになる。このプロセスは、ブロックにすべての要素が渡されるまで繰り返され、reduceはアキュムレータの最終的な値を返す
    enum.reduce(0) do |accumulator, element|
      accumurlator + element
    end
  • reduceに与えられる引数は、アキュムレータの初期値である。引数(初期値)を省略するとレシーバの先頭の要素をアキュムレータとして扱い、第2要素からイテレーションをスタートさせるが、レシーバが空の時nilを返す挙動となってしまうので、常に初期値は設定すべきである(初期値が設定されておりレシーバが空の時は初期値を返す挙動となる)
  • reduceを使えば、よく使われるmap・selectなどを使うよりも効率の良いコードにできることがある。reduceのブロックは、新しいアキュムレータさえ返していれば、与えられたアキュムレータと要素に何でも自由に操作を加えられる
 

第4章 例外

  • raiseの第1引数としてクラス名を指定できる。その場合、指定されたクラスの例外オブジェクトを作り、それを使って例外を生成する。クラス名を指定しない場合、RuntimeErrorクラスとなる。第2引数(オプション)はエラーメッセージとして使う文字列を渡すことができ、指定しない場合はクラス名となる
  • 修復方法がわかっている特定の例外だけをrescueで捕まえるようにする。例外を捕まえるときは、もっとも限定されたタイプのものを最初に書く
  • StandardErrorのような汎用例外クラスをrescue節で捕まえるのは避ける。操作を加えたいときは、ensure節で代用できないかを考える
  • rescue節の中で例外を生成するときは、raise(e) のようにして元の例外をraiseする
  • 例外が発生するかもしれないときは、確保したリソースを解放するためにensure節を使うとよい。ensure節は正常な条件でも例外条件でも実行される
 

第5章 メタプログラミング

  • クラス、モジュール、個々のオブジェクトのふるまいなどをプログラムの実行中に変更できるメタプログラミングは、Rubyのもっとも強力な機能の1つであり、もっとも危険な機能の1つでもある
 

第6章 テスティング

  • テストを書くときは、失敗するようなテストを書く。コードの重要な部分をコメントアウトし、その部分のテストが失敗するようにする
  • バグの根本原因を探し始める前にそのバグのために失敗するテストを書くバグフィックスの最初のステップはバグの再現であり、そのバグの専用のテストを用意すれば、フィックス後に再度バグが発生することを避けられる
 

第7章 ツールとライブラリ

  • Bundlerは、gemの依存を自動的に管理し、開発中に使っているgemセットとまったく同じものを他のデベロッパーや本番サーバも使うようにしてくれる。必要なgemは、Gemfileで指定する。Bundlerは、指示を受けると必要なすべてのgemとそれらの依存関係をインストールする。また、依存全体を格納するGemfile.lockを作成、更新する
 

第8章 メモリ管理とパフォーマンス

  • 実行中のRubyプログラムはオブジェクトから構成される。オブジェクトは無数にあり、たとえば、irbを新しく起動すると、10万個くらいのオブジェクトが作られ、ガベージコレクタが作動すると、アクティブオブジェクトの数は1万2千個くらいに削減される。Rubyにはガベージコレクタがあるので、マニュアルでメモリを管理する必要は無いが、ガベージコレクタが正しい処理をするためには実行中のプログラムを一時停止させなければならないので、パフォーマンスが少し下がってしまう
  • パフォーマンスの低いコードを書き換えるときは、まずプロファイリングツールを使って状況を確認するべき
  • ループ(eachブロックなど)内で使うオブジェクトが書き換えられないのであれば、オブジェクトリテラル定数とすることで、メモリ確保の回数が減り、ガベージコレクタが起動するリスクを下げることができる

 

おわりに

ここまで読んでいただきありがとうございます。 

本書にはコードを用いた具体例が多くてとても分かりやすかったので、気になる方はぜひ本書をご覧ください。 

Effective Ruby

Effective Ruby

 

次は、リーダブルコード を読む予定です。

来週も頑張ります!