技術評論社のWEB+DBPRESS vol.99の読書メモ
1章:良いコードとは
- 書き手の意図通りに動く
- 十分にテストされている。
- 読み手に意図が伝わる
- わかりやすい命名にする => 変数名、メソッド名、クラス名 ... etc
- コードを補足するための適切なコメントを書く。
- 変更に強い
- DRY => Don't Repeat Yourself (変更箇所が少なくすみ、あらゆるインプットに対応できる。)
- KISS => Keep It Simple Stupid (不要なコードは書かない。)
- YAGNI => You ain't gonna need it (不要なコードは書かない。)
良いコードを学ぶ方法
- オープンソースの良質なコードを何が良いかを考えながら読む。
2章:変数、定数、メソッド
変数
- スコープは狭く
- 変数の定義と参照の間隔は狭くする(視覚的にも、使用範囲も狭く)
- 理解しやすい変数名、適切な長さ
定数
- マジックナンバーや静的でアプリ全体で使用される情報を定義する
- マジックナンバー => Twitterの140文字のようにサービス特有の意味づけがされた数字
- 外部サービス連携用のアプリID、環境変数、エラーコード、ステータスコード ... etc
- 再利用性の高い静的情報
メソッド(関数)
- 関心を明確にする
- 適切な場所(クラス、モジュール)に書く
- 責務を単一に保ち、責務ごとに処理をメソッドに切り分ける。(疎結合なコードでテストしやすく)
- 適切な命名
- 簡潔で理解しやすい名前
- 適切な長さ(自明なgetやdoなどを省く)
- レシーバオブジェクトとの関係性も意識して命名する(品詞や助詞に気を配る)
- 関心と抽象度の合った命名(そのメソッドが本質的に何をしているのかが伝わる命名)
- 再利用性を高める
- DRYを意識しすぎて、変更しにくいコードにならないように注意する。
シソーラス辞書でしっくりくる名前を探す。
3章:クラス
クラスはシステムを構成する登場人物で、物理的実体(人や物)だけでなく、概念的実体(いいね!や通知など)もオブジェクトと言える。クラスの責務を明確にして、責務以外の処理やプロパティをクラス自身に持たないように意識する。
クラス設計手順
例)動画投稿サイト
機能:ユーザーが動画を投稿できる
名詞と動詞からクラスとその責務を考える。
名詞:クラス => 動画を
動詞:メソッド => 投稿する
- クラス(オブジェクト)
- 変数、定数(このクラスオブジェクトが持つべき値、状態か)
- メソッド(この処理はこのクラスが担当すべきかどうか)
クラス設計手順
- サービスに必要なクラスオブジェクトを考える。
- クラスが担うべき責務から処理(メソッド)を考える。
- クラス間には、どのような関係性があるか
クラスの責務におけるチェックポイント
明確な責務を持ったクラスを設計する。
- 責務が多すぎるクラスはないか
- 責務の似ているクラスはないか
- 責務の曖昧なクラスはないか
デザインパターン
客クラスがコーヒーを注文するというメソッドを持つ場合を
下二つのデザインパターンを適用した例で考える。
- Facadeパターン
- Strategyパターン
Facadeパターン適用前
客クラスはコーヒーがどのように作られているかを知っている。
class Customer
def order
cup = Cup.new
coffee = Coffee.new
cup.receive(coffee)
end
end
Facadeパターン適用後
客クラスはコーヒーがどのように作られているかを知らない。
FacadeであるBaristaクラスにコーヒオブジェクトの詳細を任せる。
class Customer
def order
barista = Barista.new
barista.brew
end
end
class Barista
def brew
cup = Cup.new
coffee = Coffee.new
cup.receive(coffee)
end
end
Strategyパターン適用前
ドリンクメニューが増えるたびに、バリスタのbrewメソッドのcase文を増やしていくことになる。
brewメソッドが果てしなく大きくなる可能性があるので良くない。
class Customer
def order(type)
barista = Barista.new
barista.brew(type)
end
end
class Barista
def brew(type)
case type
when :coffee
cup = Cup.new
coffee = Coffee.new
cup.receive(coffee)
when :cafe_au_lait
cup = Cup.new
cafe_au_lait = CafeAuLait.new
cup.receive(cafe_au_lait)
when :cappuccino
cup = Cup.new
cappuccino = Cappuccino.new
cup.receive(cappuccino)
end
end
end
Strategyパターン適用後
Baristaクラスに影響を与えることなく、ドリンクメニューの数、内容を変更できる。
それぞれのクラスの責務をシンプルに保ち、拡張性も高い。
class Customer
def order(drink)
barista = Barista.new
barista.brew(drink)
end
end
class Barista
def brew(drink)
drink.brew
end
end
class Coffee
def brew
cup = Cup.new
coffee = Coffee.new
cup.receive(coffee)
end
end
class CafeAuLait
def brew
cup = Cup.new
cafe_au_lait = CafeAuLait.new
cup.receive(cafe_au_lait)
end
end
class Cappuccino
def brew
cup = Cup.new
cappuccino = Cappuccino.new
cup.receive(cappuccino)
end
end
4章:モジュール
Rubyでは、クラスの多重継承をサポートしていないため、単一継承(is_a) + モジュール
で多重継承のような設計をカバーする。
モジュールはクラスのように継承
とインスタンス化
が使えない。
クラス階層を超えた共通の処理をモジュール化することで、複数のクラスにモジュールを組み込み、処理を使い回せる。
OCP:オープンクローズド原則
クラスは拡張に対して開いており、修正に対して閉じていなければならない。
ビジネス要件に変更があった場合に、既存コードを修正するのではなく、拡張するためのコードを追加することで対応できるように設計する。ふむふむ。
不安定なもの(具象)より、安定しているもの(抽象)への依存の方が良いとされている。ふむふむ。
モジュールの説明はこちらがわかりやすかったでやんす。
RubyのModuleの使い方とはいったい
5章:チーム開発でのテクニック
- 再利用性の高いコード(3回以上同じ処理を書いていたらメソッドに切り分けたり)
- 変化に柔軟に対応できるコード
- リーダブルコード = 理解しやすいコード(適切な命名、抽象化) + 見やすいコード(インデント、構文ブロック、改行 ... etc)
- 良いコードは全く新しく外部から入った人でもわかり、属人性が低い
- コードレビュー(思いやり、素直さ)
まとめ
良いコードを書くことも必要ですが、極力コードを書かないことも良いエンジニアの条件ですよね。