Clean Code ch3 Functions
- Functions
- Small!
- Do One Thing
- One Level of Abstraction per Function
- Switch Statements
- Use Descriptive Names
- Function Arguments
- Have No Side Effects
- Command Query Separation
- Prefer Exceptions to Returning Error Codes
- Don't Repeat Yourself
- Structured Programming
- How Do You Write Functions Like This?
- Conclusion
- 英語
Functions
- リファクタリング前後の比較
Small!
- 関数はめっちゃ短くしろ
- エディタの1画面に収まるのはもちろんのこと
- 数行程度まで
Block and Indenting
- ネストの深さは高々2重くらいまで
- 可読性・理解しやすさのため
- extract functionリファクタリングを適用し、説明的な名前をつける
Do One Thing
- 30年来で培われたアドバイス:
関数は1つのことだけをうまくやれ
- 「1つのこと」って何
- 同じ抽象度のステップに分解できていれば、それは1つのことをできている
- 異なる抽象度が混ざっていれば、それは1つのことをできていない
Sections within Functions
- 関数を複数のセクションに分解できるということは、1つのことをできていない兆候
- 【補】そのセクション名の関数群に分割しろってこと
- セクションの中身が実装に直書きされているのは、抽象度が揃っていないということ
One Level of Abstraction per Function
- 割れ窓
- 本質的な関心事と枝葉末節な詳細とがひとたび混ざると、どんどん混ざるようになる
Reading Code from Top to Bottom: The Stepdown Rule
- TO paragraphでトップダウンに記述できること
- 【補】TO paragraph: ...するには...
- 関数は動詞ないし動詞句なので
To doSomething, ...
という英文として書き下せるよという話
- 関数は動詞ないし動詞句なので
- 【補】TO paragraph: ...するには...
Switch Statements
- switch文は...
- 大きくなりがち
- N個のことをしがち
- 同じようなswitch文があちこちにあるのは最悪
- 1箇所に絞るのが理想
- Abstract Factoryの生成など
- 他の部分はポリモーフィズム
Use Descriptive Names
- よい命名はとても価値がある
- 評価してもしすぎることはない
- 1つのことだけをする小さな関数には名前も付けやすい
- 長い名前を恐れるな
- 「長くて説明的な名前」は「短いが謎掛けのような名前」より良い
- 「長くて説明的な名前」は「長くて説明的なコメント」より良い
- 命名に時間をかけるのを恐れるな
- いくつかの名前を試して、コードを読んでみるべき
- モダンなIDEならば容易くリネームできる
- いくつかの名前を試して、コードを読んでみるべき
- よい命名を検討した結果、コードの構造改善に繋がることも珍しくない
- 一貫性を
Function Arguments
- 個数
- 0個が理想
- 1個、ついで2個くらいまでが望ましい
- 3個は避けたい
- 4個以上は、相応の正当性が必要
- 関数名と抽象度の異なる引数は良くない
- テスタビリティ
- 引数が2つ以上あると、組み合わせをテストしなければならない
- 【所感】メンバ変数も同じことだと思いますけど
- out引数は普通の引数よりも認知の負荷になる
Common Monadic Forms
- 1引数関数
- good
- 関数(入力を加工して出力する)
- 例:
StringBuffer transform(StringBuffer in)
- 例:
- イベントディスパッチ
- 例:
void passwordAttemptFailedNTimes(int attempts)
- 例:
- 関数(入力を加工して出力する)
- bad
- out引数
void transform(StringBuffer out)
- out引数
Flag Arguments
- 関数にフラグを渡すのは悪い習わし
- シグネチャがすぐとっちらかる
- 明らかに1つよりも多くのことをしている
- true/falseそれぞれに対応する別の関数を用意せよ
Dyadic Functions
- 2引数
- 例:
writeField(outputStream, name)
- 例:
- 自然なまとまりではなく、自然な順番もない場合は厄介
- 例:
assertEquals(expected, actual)
- 引数を逆に渡したことは何回ありますか?(幾度となくありますよね)
- 例:
- 1引数形への変形を検討する
- 例
OutputStream
クラスのメソッドにするoutputStream.writeField(name)
outputStream
をメンバ変数に持ち、引数として渡さなくていいようにするwriteField(name)
FieldWriter
クラスを作る- 【補】Method Object Pattern
fieldWriter = new FieldWriter(ouputStream);
fieldWriter.write(name);
- 例
Triads
- 引数順の問題や認知の負荷は2引数の比ではないので十分に注意せよ
- 3引数でも気にならない例
assertEquals(1.0, amount, .001)
- 二度見不可避ではあるが、むしろそれがいい
- 浮動小数点数の比較は相対的なものであることを想起させる
Argument Objects
Circle makeCircle(double x, double y, double radius) Circle makeCircle(Point center, double radius)
- 後者は、中心点のx/y座標であるということが伝わる
Argument Lists
- 可変引数はListを渡しているのと同じ
public String format(String format, Object... args)
は実質2引数public String format(String format, List<Object> args)
と同じだから
Verbs and Keywords
assertEquals(expected, actual) assertExpectedEqualsActual(expected, actual)
- 後者は引数順を間違えない
Have No Side Effects
- 副作用は、1つよりも多くのことを隠れて行っているということ
- 時間的結合を生み出してしまう
- temporal coupling
- 【所感】「一時的結合」なんて訳もあるようだが、「時間的結合」のほうが適切でしょう
- temporal coupling
- 1つよりも多くのことをしているということなので、分割すべき
Output Arguments
- OO言語では避けるべき
- thisがoutput先
Command Query Separation
- getter/isserとsetterを混ぜない
- 混ぜると、動詞と形容詞/過去分詞が紛らわしくなったりする
if(set('username', 'unclebob')) { ... }
- setして、成功したらtrue/失敗したらfalseを返すのか
- exists的な意味なのか
Prefer Exceptions to Returning Error Codes
- 例外を使おう
- エラーコードを返すのはcommand query separation違反
- 例外を使うと、正常フローと例外フローを綺麗に分離できる
- cf. エラーコードは即座に捕まえて処理しないといけないので、正常フローがエラー処理でとっ散らかる
Extract Try/Catch Blocks
- try/catch句の中に実装を直接つらつらと書かない
- 関数に抽出する
Error Handling Is One Thing
- エラーハンドリングは「1つのこと」
- エラーハンドリングを行う関数は、tryで始まらないといけないし、catch/finally句の後に何も書いてはいけない
The Error.java Dependency Magnet
- エラーコードを返す方式だと、エラーコードが定数クラスやenumで定義されている
- こうしたクラスは、依存のマグネット(dependency magnet)と呼ばれる代物
- あらゆるクラスからuseやらimportやらされる
- エラーコードクラスに変更を加えると、依存する全クラスで再コンパイル・再デプロイが必要になる
- ので、エラーコードの追加が億劫になる
- 例外のススメ
- エラー定義を追加するときは、Exception派生クラスを追加する
- エラーハンドリング側では再コンパイル・再デプロイは一切必要ない
Don't Repeat Yourself
Structured Programming
- エドガー・ダイクストラ提唱
- one-entry one-exit
- 1つのreturn文のみ
- ループのbreak, continueなし
- gotoダメ絶対
- 【補】ガード節の早期リターンなし
- 【補】例外はone-exit違反ですね
- 大きな関数向けのルール
- 関数が小さければ、構造化プログラミングにしたがうメリットは少ない
- むしろ、破っても害がないばかりか、表現力が上がるまである
- ただし、この場合もgotoはやめよう
- 小さな関数では役に立たないし
How Do You Write Functions Like This?
- 文章を推敲するのと同じ
- 最初は不細工なコードでよい
- 深いインデント
- 多重ループ
- 引数いっぱい
- 名前に一貫性がない
- 重複
- 単体テストで全行カバーして、きれいにしていく
- 関数分割
- リネーム
- 重複をなくす
Conclusion
- システムはドメイン特化言語で構築される
- 「プログラムを書く」というよりは、「物語を語る」
- そのための語彙にフィットする関数やクラスをつくる
- 関数は動詞
- クラスは名詞
英語
- arcane
- 秘密の
- accrete
- 固まる、融合する
- a host of
- たくさんの
- a lot ofとかの仲間?
- たくさんの
- double-take
- 少ししてから驚く
- 「ふ〜ん…えっ」ってやつ
- 少ししてから驚く
- proclaim
- であることを明白に表す
- monadic
- 引数が1つの
- dyadic
- 引数が2つの
- triadic
- 引数が3つの
- come at a costs
- 高くつく
- evocative
- 喚起する、示唆に富む
- devious
- 遠回りの、曲がりくねった
- wind up with
- ...で締めくくる