【書籍】良いコード/悪いコードで学ぶ設計入門
はじめに
設計について興味がありまして、友人に勧められて読んでみました。
設計についての基礎知識が身についていない状態で実装してしまうと、仕様変更時の際にかなり苦戦したので、次回からは学んだ内容を活かして、案件に取り組みたいと思います。
はじめに、第一章 メモ
はじめに
・あるべき構造を知ると、よくない構造をはっきり知覚出来ます。
第一章
悪しき構造の弊害を知覚する
・設計の重要さを知覚するには、設計をないがしろにするとどんな弊害が起こるのかを知ることが第一歩です。
弊害の例:
1.コードを読み解くのに時間がかかる
2.バグを埋め込みやすくなる
3.悪しき構造がさらに悪しき構造を誘発する
・型名を表すInt、メモリ制御を表すMemoryやFlagなど、プログラミング用語やコンピューター用語にもとづいた技術ベースでの命名を技術駆動命名と呼びます。
例:
intValue01 -= changeValue;
・クラスやメソッドに対して番号付で命名するのを連番命名と呼びます。
例:
void method001();
void method002();
void method003();
・技術駆動命名や連番命名は、意図がまったく読み取れない、悪しき手法です。
こうしたコードは理解が難しくなり、読み解くのに時間がかかる。
理解が不十分なまま変更するとバグ化します。
・関連するデータやロジックどうしが分散し、バラバラになっているのを低凝集と言います。
・低凝集によって引き起こされる弊害
1.意図せず重複コードが量産される可能性がある。
2.修正漏れが生じ、バグとなってしまう可能性がある。
3.可読性が低下する。
4.未初期化状態(生焼けオブジェクト)
初期化しないと使い物にならないクラス、未初期化状態が発生しうるクラスを、アンチパターン生焼けオブジェクトと呼びます。
5.不正値の混入
不正とは、仕様として正しくない状態を指します。
1.注文数がマイナスになっている。
2.ゲームにおいて、ヒットポイントの値が最大を超えてしまっている。
第二章 メモ
第二章
設計の初歩
・省略せずに意図が伝わる名前を設計しましょう。
・変数を使い回さず、目的事の変数を用意します。
変数に再度値を代入することを再代入と呼びます。
再代入はコードの途中で変数の用途が変わってしまうので、読み手が混乱し、バグを埋め込んでしまう可能性があります。
・意味のあるまとまりでロジックをまとめ、メソッド(関数)として実装しましょう。
・保守しやすい、変更しやすいよう、変数の名前やロジックに工夫を凝らすことが設計です。
・関連し合うデータとロジックをクラスにまとめましょう。
第三章 メモ
第三章
クラス設計
・クラスを適切に設計することが、複雑で難解な条件分岐やメソッドの構造改善につながります。
・クラスが単体で正常動作するよう設計する。
良いクラスの構成要素
・インスタンス変数
・インスタンス変数を不正状態から防御し、正常に操作するメソッド
・自分の身は自分で守らせる。自己防衛責務をすべてのクラスが備える、という考え方がソフトウェア品質を考える上で重要です。構成部品であるクラス一つ一つが品質的に完結していることにより、ソフトウェア全体の品質が向上します。
・処理の対象外となる条件をメソッドの先頭に定義する方法をガード節と言います。
・インスタンス変数を不変(イミュータブル)にします。不変にはfinal修飾子を使います。
・途中で値が変化すると、どう変化したのか追うのが難しくなりますし、バグの原因にもなります。
基本的に引数は変更するものではありません
引数にfinalを付けると不変になります。
・高凝集化したり、不正状態から防護したりなど、プログラム構造を改善する設計手法を設計パターンと呼びます。
第四章 メモ
第四章
不変の活用
・変数の値を変更するなど状態変更できることを可変(ミュータブル)と呼びます。
一方状態変更できないことを不変(イミュータブル)と呼びます。
・変数に再度値を代入することを再代入、または破壊的代入と呼びます。
再代入は変数の意味が変わり、推測を困難にします。
また、いつ変更されたのか追うのが難しくなります。
途中で意味が変わると読み手が混乱します。誤解してバグを埋め込む可能性もあります。再代入は避けるべきです。変数の上書きをせず、別の変数を用意することで再代入を防げます。
・ローカル変数にfinal修飾子を付けると不変になり、変更できなくなります。
・引数も不変にする。
・副作用のデメリット
副作用とは、関数が引数を受け取り、戻り値を返す以外に、外部の状態(変数など)を変更することです。
関数には、主作用と副作用があります。
主作用:関数(メソッド)が引数を受け取り、値を返すこと
副作用:主作用以外に状態変更すること
状態変更とは
・インスタンス変数の変更
・グローバル変数の変更
・引数の変更
・ファイルの読み書きなどのI/O操作
・オブジェクト思考プログラミング言語では、副作用のない関数を厳密につくり込むスタイルよりも、クラスのスコープ内で影響を閉じ込めるスタイルが一般的です。
第五章 メモ
第五章
低凝集
・凝集度とは「モジュール内における、データとロジックの関係性の強さを表す指標」です。
高凝集な構造は、変更に強い、望ましい構造です。逆に低凝集な構造は、壊れやすく変更が困難です。
コードが散らかっていると、何がどこにあるのかわからなくなる。
・ロジックとデータをひとつのクラスに集めて高凝集に設計する、オブジェクト指向設計の基本です。
・引数が多すぎるメソッドは、低凝集に陥る良くない構造です。
・引数が多すぎる事態に陥らないためには、概念的に意味のあるクラスをつくることが肝要です。
・ソフトウェア設計には、尋ねるな、命じろという有名な格言があります。
呼び出し側はただメソッドを命ずるだけで、命令された側で適切な判断や制御するように設計しましょう。
第六章 メモ
第六章
・if文が入れ子構造になっている事を
ネストしていると呼びます。
ネストが深いと言ったりもします。
例:
if(){
if(){
}
}
・コードの見通しが悪くなり、どこからどこまでが
if文の処理ブロックなのか、読み解くのが難しくなります。
理解が不十分なままでロジック変更するとバグになります。
・ネストの悪魔を退治する手段のひとつに、早期returnがあります。
条件を満たしていない場合に、ただちにreturnで抜けてしまうという手法です。
・else句も、見通しを悪化させる要因のひとつです。
・interfaceを使いこなせるかが、設計スキルの分水嶺といっても過言ではありません。
分岐:
初心者、迷わずif文やswitch文を使う
中級、interface設計を試みる。
分岐ごとの処理:
初心者、ロジックをベタ書きする。
中級、クラス化を試みる。
第七章、第八章 メモ
第七章
・車輪の再発名
すでに広く使われ確立している技術や解決法が存在しているにもかかわらず、知らないか、または意図的に無視して
新たに同じようなものを作り出してしまうこと
・四角い車輪の再発名
すでにあるものよりも役に立たないものをつくり出すこと
第八章
・結合度とは「モジュール間の、依存の度合いを表す指標」です。
・あるクラスが、ほかの多くのクラスに依存している構造を密結合と呼びます。
密結合なコードは理解が難しく、変更が非常にやっかいです。
・単一責任の原則、クラスが担う責任は、たったひとつに限定すべき
・DRY原則
繰り返しを避けよ
・継承は、よっぽど注意して扱わないと危険、継承は推奨しません
第九章 メモ
第九章
・どんな条件であっても決して実行されないコードをデッドコード、または到達不能コードと呼びます。
・YAGNIと呼ばれるソフトウェア原則があります。実装に必要になったときにのみ実装せよ、という方針です。
・今必要な機能だけをつくり、構造をシンプルにしましょう。
・ロジック内に直接書き込まれている意図不明な数値をマジックナンバーと呼びます。
マジックナンバーは実装者本人にしかほとんど意図を理解出来ません
・可能な限りグローバル変数にはせず、限られたクラスだけがアクセス可能なように設計しましょう。
・nullを返さない、nullを渡さない
nullを返さない設計とは、メソッドの戻り値としてnullをreturnしないことです。nullを渡さない設計とは、nullを変数に代入しないことです。
・プログラム実行時に、そのプログラム構造自体を制御するプログラミングをメタプログラミングと呼びます。
・サンプルコードをそのままなぞって実装すると、設計上良くない構造になりがち
・設計にbestはありません。常にbetterを目指しましょう。
第十章、第十一章 メモ
第十章
・名前をないがしろにすると、責務が入り乱れて密結合になったり、巨大化して神クラスになったりします。
・目的駆動名前設計、ソフトウェアで達成したい目的をベースに名前を設計(命名)します。
・商品クラスは関心事それぞれのクラスへの分割が必要
・クラスやメソッドに名付けることを、「命名」ではなく、「名前設計」と好んで呼称しています。
ここでの設計は「ある課題を解決するためのしくみや構造を、考えたり、つくり上げたりすること」
・プログラミングにおける名前の役割は、可読性を高めることだけではない
関心の分離を意識し、ビジネス目的に沿った名前を付与することは、疎結合高凝集を実現する上で重要です。
・ラバーダッキング、プログラミングで何か問題が発生した時に、それを誰かに説明すると自ら原因に気付き
自己解決する手法です。
・チーム開発においては、命名が重要であり、名前とロジックが対応する前提であること、名前がプログラム構造を大きく左右することをチーム内で約束しましょう。
・技術ベースでの命名を技術駆動命名と呼びます。
技術駆動命名は意図が分かりにくくなります。
・基本的に名前は省略しない事
第十一章
・実装と比べてコメントの情報が古くなった時点で、コメントは嘘をつきはじめます。
情報が古くなり実装を正しく説明しなくなったコメントを退化コメントと呼びます。
・コードはいつ、どういう目的で読まれるのでしょうか
機会として最も多いのは。保守と仕様変更時です。
コード保守の際、読み手が気にするのは「このロジックはどういう意図で動いているのか」です。
仕様変更の際、読み手が気にするのは「何に注意すれば安全に変更できるか」です。
・コメントルール
1.ロジック変更時、同時に必ずコメントも変更する事
2.ロジックの内容をなぞるだけのコメントをしない事
3.可読性の悪いロジックを補足説明するようなコメントをしないこと、代わりにロジックの可読性を高める事
4.ロジックの意図や仕様変更時の注意点をコメントする事
第十二章 第十三章 メモ
第十二章
・メソッド設計の良し悪しはクラス設計に密接に連動します。
・よそのクラスを気にしたりいじったりするメソッド構造は低凝集構造です。
・カプセル化とは、データとそのデータを操作するロジックを1つのクラスにまとめ、必要な手続き(すなわちメソッド)のみを外部へ公開する事です。
・引数は不変にする事
・引数にnullを渡さない設計にしましょう。
・引数が多くなりそうな場合、別クラスへの分割を検討しましょう。
・nullを返さない
・ある値に複数の意味を持たせることをダブルミーニングといいます。
第十三章
・動作原理やしくみを簡単に理解・説明するために、物事の特徴や関係性を図式したものをモデル、モデルをつくる活動をモデリングと呼びます。
・モデリングをしないと、変更に弱く、悪魔を呼び寄せるコードを容易に書いてしまいがちです。
・単一責任の原則「クラスが果たす目的は、たったひとつに限定すべき」
・特定の目的に特化して設計することで、変更に強い高品質な構造になるのです。
第十四章 第十五章 第十六章 第十七章 メモ
第十四章
・ユニットテストとは小さな機能単位で動作検証するテストの総称です。
・試行リファクタリングとは正式にリファクタリングするのではなく、ロジックの意味や構造を分析するためにお試しでリファクタリングするものです。
・機能追加とリファクタリングを同時にやらない事
・リファクタリングを行うことを、「リファクタする」と言います。
第十五章
・ソフトウェアにおける設計とは、なんらかのソフトウェア品質特性の向上を促進するためのしくみをつくることです。
・変更が困難で壊れやすいコードをレガシーコードと呼びます。
レガシーコードが蓄積している状態を技術的負債と呼びます。
・エンジニアにとっての本質的な資産とは技術力にほかならないです。
・課題を知覚できないと、そもそも設計しようとする意識すら生まれないのです。
・コードの複雑さや、可読性などの一連の品質指標をコードメトリクス、またはソフトウェアメトリクスと呼びます。
・人間の短期記憶は一度に4+= 1個の概念しか把握できない
この個数をマジカルナンバー4と呼びます。
・クラス設計をする際は、マジカルナンバー4を援用して、脳に優しい構造を心掛けましょう
・ウリになるビジネス領域をコアドメインと呼びます。
第十六章
・あるべきシステム構造と組織構造に違いがあると、あるべきシステム構造をつくり上げるのが困難になるのです。
・仕様変更の際、最低でもメモ書き程度のクラス図を書きましょう。
・メンバー間の能力差が大きい場合は、多数決ではなく、シニアエンジニアなど設計スキルの高いメンバーが中心となってルールを作りましょう。
そしてチームリーダーの権限で、ルールの遵守を推進していきましょう。
・既存コードを信用せず、冷静に正体を見破る
第十七章
・3日後の自分は他人
プログラミングの世界にはこんな言葉があります。
・インプットは2、アウトプットは8
最後に
ざっくりと書籍を読み、設計やプログラミングの基礎的な知識が身に付きました。
次回の案件が始まる前にざっと復習をしてから実装します。
Discussion