プログラミング設計論

こんにちは〜
ソフトウェアエンジニアをしております秦野です。
今回の記事は「プログラミング設計論」と題しまして、どのようにプログラムを設計すべきかという話をしようと思います。
 
そもそもなぜこの記事を書こうかと思ったかと言うと、プログラミングの設計は「一番重要」なことなのに何故かそれがいつも置いてけぼりになっているように感じたからです。
設計を重視せず、悲惨なコードに苦しんでいるエンジニアが一杯います。
 
まず、何故プログラミングの設計がエンジニアにとって「一番重要」と言えるのかを簡単に説明しようと思います。
エンジニアの仕事とは何でしょうか?エンジニアの仕事は言うまでもなくコードを書くことですね。もちろん他にも、仕様書作成であったり、打ち合わせなど他の仕事もありますがそれも全てコードを書くためにあります。
そして、コードを書くためにはコードを読まなければなりません。
出来るだけ事業スピードをあげるためには、沢山の機能を追加するためにはコードを読む時間ではなくてコードを書いている時間の割合が高くなるようにしなくてはなりません。
また、同じ機能を追加するにしても出来るだけ短い時間でコードを書けた方がいいことも言うまでもありません。
そして、コードを読む時間とコードを書く時間に関係するのが「どのようにコードが書かれているか、どのように書くか?」つまり、「どのように設計されているか、するか?」ということです。
つまり、設計の良し悪しがエンジニアの生産性に直結しているのです。
ビルゲイツに言わせるとエンジニアの能力差は100倍あるそうですが
全くもってその通りだと思います。設計スキルが低すぎて機能追加できなかったり、バグだらけのコードを書いてしまうエンジニアは一杯います。
エンジニアの能力差は無限にあると言ってもいいぐらいです。
 
それでは本題の「プログラミング設計論」の話に入ろうと思います。
「プログラミング設計論」というのは言い換えれば「プログラミングデザイン論」とも言えると思います。
ではデザインとは何でしょう?
私の中でデザインとは一言で言えば相手に情報を分かりやすく整理して伝えることです。
つまり、プログラムを設計する、書くと言うことは読み手にとっていかに分かりやすく書くか?ということに尽きると思います。
 
そこでどのように書くべきか?という問題にぶつかります。
私としての答えは使い手(読み手)にとって分かりやすいインターフェースとなるように書くべきだということになります。
つまり、カスタマーサイドに立って書こうということです。
ハンバーガーを売ってる訳でもないのにカスタマーサイドに立つ必要があるのか?と思うかもしれませんが、ちゃんと読み手のことを考えないコードが汚いコードになるのは必然です。
 
もしも「プログラミングする時に何を一番意識して書いていますか?」と聞かれれば「クライアント側から見て、すぐに理解できるかを意識しています。」と私は答えます。
プログラミングする時はインターフェースが命です。
インターフェースが全てです。
ある一連の処理を行うプログラムがあった時、それを一番上のメソッドから読み下した時に何を行なっているか大体分かるように書くべきなのです。(ここでは説明のため一旦メソッドをインターフェースとします。)
もっと言えば、普通の英語の文章を読んでいるようにプログラムを書くべきなのです。
プログラミング初心者でも、プログラミングを知らない人でも「ああ、ここではこういう処理を行なっているんですね。」と分かるようにすべきです。
もしも分からないのだとしたら、オブジェクト指向で書けていない、クラス分けやメソッドへの分離、メソッドの命名などが上手くいっていないのです。1つのメソッドに責務を詰め込みすぎているのです。
 
定食屋の自動券売機を考えてみましょう。
定食屋の券売機で食券を買う理由は何でしょう?
「食べたいものがあるから」ですね!
では、食券を買う時に食べたいもの以外に興味があることはありますか?
例えば、レシピ・物流経路・お店の誰が調理するか・お皿の種類などに興味がありますか?
私はありません。
レシピ以外に興味があるのは「いくらか?」というものぐらいです。
 
ではここで「券売機にお金を入れて、食券のボタンを押すと料理を提供する」というサービスをメソッドとして定義してみましょう。
大体次のようになるでしょう。
解説用のコメントを入れていますが、簡潔なインターフェースを定義できているので本来はコメントは必要ありません。 

def serve_cuisine(ticket_type, charge)
  # まず、入力値をチェックするべき、不正な値ならそこから先の処理に進めてはならない
  raise Error('お金が足りないよ') unless enough_charge?(ticket_type, charge)
  chef = ChefRegistry.get_free_chef
  cuisine = chef.cook(ticket_type)
  serve_customer(cuisine)
end

これをインターフェースを意識しないエンジニアが実装すると、上から下に読んでも理解できないコードを書いてしまいます。
例えば、シェフを取得するコードで、例に挙げたコードではChefRegistryクラスを定義し、DBへのアクセス、アクティブレコードへの明示的なアクセスを避けて、高度に抽象化したメソッドを定義してそれを利用するようにしています。
このようにメソッドを定義することで次の利点があります。
クライアント(読み手、ユーザー)は裏側の知識なくシェフを取得することができます。ChefRegistryクラス内でアクティブレコードへのアクセスは隠されているので、アクティブレコードではない別のORマッパーを利用しても、全別々のNoSQLを利用しても、ファイルに読み書きするようにしてもユーザー側のコードは壊れません。
また、Chefの振る舞いではない、DB層からのChefインスタンスの生成という処理を別のクラスに分離できています。これを分離せずChefに振る舞いと関係ないメソッドを含めてしまうと、Chefクラスが肥大化して理解しにくいコードとなってしまいます。
つまり、知る必要がないことが上手く隠されている上、必要十分な情報が公開されており分かりやすく、柔軟性が高い、疎結合で凝縮性の高いコードになっているのです。

しかし、インターフェースを意識しない、クライアントのことを考えないプログラマーが書くコードはアクティブレコードに直接アクセスし、様々な条件分岐を行うような部分をユーザー側に公開してしまいます。1行で行なっているコードが10行以上の表現となってそんな実装の詳細を知らなくてもいいクライアント側のコードに書き殴られてしまうのです。
自動券売機の例で言えば、こちらが求めていない、レシピや物流経路、担当しているシェフの名前などを知らされるようなものです。
そんなことはどうでもいいからクライアントが必要なものを手軽に届けて欲しいと思います。
他のコードの部分についても同様です。
それぞれ1行で済むようなコードが何十行にもなって表現され、このメソッドって何をするためのものなんだっけ?このコードとこのコードの関係はどのような関係なのか?関係あるのか?ないのか?インターフェースが明確でないと機能の役割分担が明確でなくなり非常に分かりづらいコードとなってしまいます。

ここまで、プログラミング設計論と言いつつ、インターフェースの話しかしていませんが各種のプログラミング論、オブジェクト指向プログラミング、関数型プログラミングMVCアーキテクチャ、様々なアーキテクチャ論は全てインターフェースの話を、つまり設計のデザインの話をしています。
いかに人がコードやシステムを理解しやすいようにするか。という問題に対して全て発展してきたものです。
だから、プログラミングの設計をする、プログラミングの設計を学ぶときはインターフェースを分かりやすくするためにしているんだという視点を忘れないで欲しいと思います。
基本をまとめると
インターフェースは
・意味のある言葉として一目で理解できる
・不必要な情報は見せない
・役割が明確である
以上の点を守る必要があります。
また、これらが守られていれば次のような利益を得ることができます。
・理解しやすく、コードを読む時間を少なくすることができる
・どこで何をしているか明確で、必要な機能を必要な場所に素早く追加できる
・機能修正が必要な時も素早く修正できる上、他の部分への影響が少ないか全くない
・部分的な機能を抜き出しやすい(モジュール化・マイクロサービス化しやすい)

話が大きくなり過ぎるのを防ぐために、コードベースで話をしましたが、どのようなシステムにしろそれが他のもの、例えばAPIや他のサービスと連携するようなものならインターフェースの重要性に変わりはありません。
だから私の今回の話はシステム設計の一般論を語っているものとも言えるかも知れません。
今回はプログラミングに関しては1回目の投稿でしたので、基本であり最終奥義でもあるインターフェースの話をしました。
インターフェースを上手く表現できるようになると、つまり設計能力が高くなるとどんなプログラミング言語でも上手く操れるようになります。
新しいプログラミング言語を始めても直ぐに上達して、美しいコードを書くことができるようになります。
プログラミングを学ぶこと、書くことがどんどん楽しくなります。
だからプログラミング言語やソフトウェアの使い方だけを学ぶのではなく、プログラマの地力と言える設計能力を高めることが重要だと思います。

今回は理論にほぼ費やしましたので次回からはもう少し具体的な例を出していきたいと思います。

最後までお読み頂きありがとうございます。