猿神大学

数学についてあまり書きません。

『プリンシプル オブ プログラミング』を読んでみての感想と個人的な備忘録

はじめに

三角関数ちゃそです。

 

GWに突入したので「遊ぶぞ~」と思っていたのですが、最近の仕事の多くが「悪路をダート自転車で走る」みたいなコーディングばっかりで楽しくなかったので、休みと言ってもある程度プログラミングのに関連した勉強もこなそうと思いました。

 

約10日の長期休暇ですが、2~3回投稿出来たらいいなあ。多分無理。

諦めるのが早い?

早いうちに懸命な判断を下せるんですよ?

 

 はい。

 

今日は1回目。

 

この本を読みました。 

 

www.amazon.co.jp

 

いや、まあ、はじめてこの本を読んだのは1年くらい前なのですが。

 

この本って、「プログラミングの原理原則!」と言いながら、肝心のコードは出てこなくて、内容がいかんせん抽象的なんですよね(Amazonにも似たような批判があります)。

 

僕が思うに、その抽象性の高さは、多分「JavaにもCにもPythonにも共通して言えることをまとめました!」というところに起因してそうですが…。

 

「The principles of programing」と銘打っているだけあって、特定の言語に偏るような表現をなくしていった帰着だと思うんですが、それゆえ抽象的な表現、すなわち行間が多いです。

 

行間を埋めるには経験と知識が不可欠だと思うのですが、1年くらい前の僕はプログラミング未経験で、かつ現場に出ていないHIYOKOだったので、読んだ当初はこの本の内容の抽象さに躓きました。

タイトルに「プログラミング3年目までに身につけたい」とあるわりには、初心者には難しい内容かと思います(これって私の感想ですよね)。

 

(批判的な感想を書いていますが、本の内容としては勉強になるものが多かったです。例えば、『○○言語で読み解くプログラミング原理原則』みたいな本があったとして、それに記述された具体的なコードを参考にしつつ読む、とかなら初心者でもわかりやすいのかな?と思ったり。)

 

で。

2年目にもなると、ある程度現場に出て、

 

は?何このクソコード(血塗られた文字色で怒りを表現)

 

といった気持ちが、ある程度経験値が蓄積されてきたわけです。

 

そこで、『プリンシプル オブ プログラミング』の2周目を読んでみたわけです。

 先ほど、「行間を埋めるには経験と知識が不可欠」と書いたのですが、それに倣うわけですね。

 

今回は2周目を読みながら自分で考えた内容のメモです。

 

(当然ですが、感想ですので、この本の著者と異なる解釈が含まれます。ご了承ください。)

内容のメモ

そもそもプログラミングの原理原則とは

  • コードは変更されるものである(新規機能の追加、バグの発見・改修、新技術の採用、既存システムのサポート終了…)。
  • 変更されることがわかっている以上、変更に強い設計が求められる(この本でいう設計は、基本設計~テスト・デバッグまでの一連の流れを指している)。
  • 変更に強い設計を行う際に肝になる設計指針が、いわゆる「プログラミングの原理原則」。

プログラミングの原理原則を重んじることで、変更に強いコードが書ける。

 

プログラミングの原理原則

この本に書かれているプログラミングの原理原則。*1

KISS(Keep It Simple, Stupid.)
  • 三者による変更のしやすさを担保するために、コードはシンプルに書く。無駄に遠回りな手段を取らない。
  • 後述のPIEとのバランスが重要そう。
DRY(Don't Repeat Yourself.)
  • 修正箇所・範囲を拡大化させないために、同じ処理を繰り返し書かないようにする。同じ処理は共通化して切り出す。
  • 過度な抽象化をすると、アンチパターン誘発の恐れがあるので、抽象化は一定の力感覚で行った方がよさげかも(後述のSLAPとのバランスが重要そう)。単一責任の原則も意識して、適切な力加減で抽象化ができるといいのだろう。
YAGNI(You Aren't Going to Need It.)
  • 三者による変更のしやすさを担保するために、コードは必要最低限のもののみを書く。拡張性を考慮しすぎない。
  • 「拡張性を考慮しない」というのはインターフェース・基底クラスの作成を躊躇させるが、そこは多態性との天秤をかける必要があるのでは。
PIE(Program Inttenly and Expressively.)
  • コードの意図を明確に表現するため、コードの可読性を意識する。
  • コメントを書く。第三者に伝わるように。
SLAP(Single Level of Abstraction Principle)
  • コードに要約性と閲覧性を持たせるため、抽象度のレベルを合わせる。
  • 構造を見たときに、書籍の目次に近い構成になっているのがベター。
OCP(Open-Closed Principle)
  • コードの変化を波及させないために、「拡張に対して開いている」「修正に対して閉じている」設計を意識する。
  • 例えば、MVCモデルとかは役割に応じてレイヤの切り分けがなされるため、修正に対して閉じていることが多い(表示方法を変更したいときは表示を行っているところを修正したら十分)。OCPを理解するには、プログラミングの文法やアルゴリズムよりも、実際に設計してテストを書いてみるのが手っ取り早そう。
名前重要(Naming is important.)
  • 可読性向上のため、関数やクラスには適切な名前を付ける。

プログラミングの原理原則を列挙したが、「ある原則を誤って解釈してしまう」「ある原則を過度に軽視あるいは重視してしまう」だけで、コードの設計指針がふわふわになってしまいがち。原理原則についての力加減は一定にしたい。

 

プログラミングのイデオロギー(一部)

上述のプログラミングの原理原則を達成するために必要な心構え。個人的に大切にしたい観点をメモ。

単純原理
  • 単純なやり方を優先する。無駄に難しいことはしない。
  • 複雑で全体的な関連性を意識しない。例えば、クラス内の処理はクラス内で完結させる。
  • 部分的に改修するときは、ある機能から呼ばれたときのための特殊なメソッドとか作りたくなるけど、それはアンチパターン
同型原理
  • 同じことを同じように扱う。コードに一貫性を持つ。
  • 例えば、ユーザ情報に関するインターフェースを作成したときに、所属グループ情報に関するインターフェースも作成する、など。
対称原理
  • 形の対称性にこだわる。
  • 例えば、命名時に「write」↔「read」、「input」↔「output」とする、など。
階層原理
  • 主従関係、前後関係、本末関係などの階層関係を意識する。
  • 例えば、クリーンアーキテクチャMVCモデルなどは、役割や抽象度で階層を作る。
線形原理
  • 処理の流れは直線にこだわる。過度な分岐を作らない。
  • 例えば、条件を満たしていないものは早期returnする、とか。また、フラグ引数はアンチパターンと言える。
明証原理
  • 少しでも不明瞭なロジックには、明証を行う。KISSやPIEを意識しても、ムズカシイ処理というのは出てきてしまう。その場合は、別途資料を作成するなどして、明証に努めるべき(もちろん、コード内で済むのであればそれが一番なのだが)。
  • 論文で言ったら、「証明を書く」「参考文献を挙げる」みたいなもの。ロジックが正しいことの責任の所在を明らかにすることで、コードを読む側に安心感を与える。
安全原理
  • ありえないことも考慮して、安全性を保つ。
  • エラー処理、分岐、nullチェックなど。「この処理が呼ばれるときは○○な状況はありえないから」と言って安全性を担保した気になると、バグが発生する。
  • テスト駆動開発は、安全原理を身に付けるうえでも、やってみて面白いのではないか。
 

 所感

具体例もあっていいな、と思ったメモが以下。

qiita.com

 

qiita.com

 

プログラミング初心者だと、プログラミングの原理原則は衝突しやすいことが多い気がする。

例えば

  • KISSを意識しすぎたあまり、「最低限のコードしか書かない」となって、インターフェースや基底クラスの作成を怠った結果、同レベルのメソッドやクラスが見えづらくなり、結果SLAPが疎かになる。
  • DRYを意識しすぎたあまり、抽象化されすぎて「似たような処理なら何でもやってくれる魔法のようなクラス」が生まれてしまい、その名前が「○○Manager」で、名前から役割がわからない状態になる(こういう時は大体OCPも守られていない)。単一責任の原則が軽視された末路。
  • 過度なOCPのせいでインターフェースや基底クラスが増え過ぎてKISSが疎かになるだけでなく、結果各クラスの役割が見えてこないで、PIEが損なわれる。

など。

 

文法を知らないとアーキテクチャに関連した知識は学べないことが多いから、アーキテクチャが後手になるのは仕方がない。

 

それは理解した上で、文法も知らない初心者に対して、例えば

  • ネットの「いかがでしたか?」系の記事
  • 入門書のサンプルコード

は、プログラミングの原理原則を遵守したいキモチの見えない設計が多い気がする。

なんというか、一問一答形式のコーディングが多く、サンプルコードごとに繋がりが見えづらい。繋がりが見えてこないと、アーキテクチャを今後学ぶ上で、アーキテクチャに関する意識が育まれない気がする。

 

僕自身、オブジェクト指向のインターフェースのメリットを教わる際に、

 

モンスターは「攻撃する」という動作があるから、「攻撃するインターフェース」を切ることで、それを継承した先に「攻撃する動作がある」ことを約束することができる

 

といった例を引き合いに出されたのだが、

  • それはモンスター基底クラスを作成して、そこに「攻撃する」を付与するのとどう変わるのか?
  • そもそも、「特定の動作を付与できる」ことの何が嬉しいのか?

というところが上手く解消されないまま、オブジェクト指向の勉強を終えてしまった。

 

もちろん、「攻撃する」という視点で抽象化を図ることはメリットなのだが、ゲームのように「攻撃されてHPが減って、…」みたいな繋がりが生まれる段階までコーディングをするのは、初心者にはその全貌が見えづらいのでは?と思ったり。全貌が見えないと、「攻撃する」は単体の孤立した動作のように見えてくる。

 

個人の経験だが、オブジェクト指向「クラス」という概念に引っ張られているからか、「モンスター」や「動物」、「果物」というテーマで説明されがちな気がするなあ。今回取り上げた『プリンシプル オブ プログラミング』と違って、中身が具体的すぎて、結果視野が狭まる気がする(抽象化に対するアンテナがぶれやすい)。

 

オブジェクト指向の説明を行う対照が「モンスター」ではなく「UI」であるだけで、インターフェースや基底クラスの理解の仕方は変わると思う。

 

例えば、何かしらの検索ツールをつくるとき、検索用のデータは

  • ローカルのファイル(csvxmljson、…)
  • データベース
  • Web API

などなど、いろいろなところを参照できると思う。

 

ここで、何を参照するかはさておき「検索する」という動作を抜き出して、適当なモデルに関連したインターフェースの実装をしてみる、テストも書いてみる、とかがいい実践になるんじゃないか、と思ったり。

その結果、プログラミングの原理原則と向き合えたら、すごくいい勉強になるんじゃないかな。

 

まあ、知らんけど。

 

余談

プログラミングの原理原則って、プログラミングに限らず、数学の論文執筆にも有用だったりするのかもしれない。

  • KISS:無駄に遠回りな手段を取らない。
  • DRY:繰り返される等式変形は補題として切り出す。
  • YAGNI:仮定や条件をなるべく抽象化し、必要最低限のもののみを書く。拡張性を考慮しすぎない。
  • PIE:適宜、脚注や付録、参考文献を付与する。
  • SLAP:抽象度のレベルを合わせる。似たような補題の証明は、似たような流れで行う、など。目次を出した時がわかりやすい。
  • OCP:仮定や条件を抽象化することで、「拡張に対して開いている」「変更に対して閉じている」定理になる。
  • 名前重要:可読性向上のため、関数や変数には適切な記号を付ける。

最後に

 

いかがでしたか?

 

今日は、まじめっぽい記事を書いてしまった。

 

面白いギャグがなかったので、個人的には0点。

 

自分にストイックな姿勢が評価されてモテろ。

 

モテろ。

 

*1:以下のURLとかも参考になりそう。

www.makeuseof.com