What is it, naokirin?

プログラミングで考えていることについて考察してみる

まえがき

前回までDDDの読書記事書いてたのですが、7章くらいまで読んだあたりで先にやりたいことができてしまったので、一旦中断しています。(6, 7章は年末年始の間に記事書きます)

今回は年末ということで今年の振り返り的な記事を書きます。

はじめに

今回は私のプログラミング時の考え方などを見なおして、より良いものにするためのヒントを見つけるための作業を行おうと思っています。この思考の見直し、という点で非常に参考になったが、今年読んだ本のうちの一冊、「実装パターン」です。「実装パターン」はケント・ベック氏の著書で、残念ながら現在日本語訳は古本を探すなどしか無いのが現状です。

実装パターンの第1章、「はじめに」にはこのような言葉が書かれています。

コードを通して考えを伝えるには, 行くるかのステップが必要だ.
(中略)
しかし, あることに気がついて驚いた. プログラミング上の決定がスムーズにすばやく行われているのに, なぜメソッド名をそうすべきと思ったのか, そのオブジェクトにロジックを含ませるべきだと確信した理由はなんだとかが, 説明できなかったのだ.

これには私も思い当たる点がありました。 なぜその名前を選んだのか、なぜこのように分離したかというのは、直感的な判断に大きく左右されていて、後で結果として「あの部分はあの実装にしてよかった」だとか「あの命名は間違いだった」とか、そういうことをやっているのが思い当たりました。さらに気がついたことは、どう考えて決定しているかを知らなければ、私自身の思考の問題点や改善点を知ることすらできないのではないかということでした。

そこで、私がプログラミングをしている時に考えていること、判断の基準としてつかっていること、それらを洗い出してみることにしました。「実装パターン」に書かれていることにかなり近い部分も多くあります。また、アンチパターンになりうるような部分もあるのではないかと思います。ここで書いていることは、私が私自身の思考を見つめなおすためであると考えてください。

対象のプログラミング言語はなにか

「実装パターン」という書籍ですが、実際には「Javaにおける実装パターン」と銘打ったほうがよいくらいにはJavaに依っている部分があります。つまるところ、オブジェクト指向C言語ライクな条件分岐Java特有のライブラリ機能 などに依っています。

今回、私もよく使っているC++C#を前提に話を進めます。ただ、それぞれの言語機能に依っているよりはオブジェクト指向を含む言語で共通なことが多くなると思います。 (いろんな言語で実装パターンみたいなのがあると面白そうですね)

かんがえていること

頭のなかで考えていることについて書き出していきます。

インターフェースは最小化させる

ここでいうインターフェースはオブジェクト指向言語のインターフェースというよりは、他のモジュールやクラスに公開されている機能と言った意味合いです(そのため、オブジェクト指向言語におけるインターフェースにも言えることになると思います)。

インターフェースを最小化することで、不必要に詳細な実装を知ることなく、機能を利用できるように実装できると考えています。これは他の人がその機能を利用する際も非常に役に立ちますし、後で自分がその機能を利用するときも、無駄な利用を考える必要がなくなります。また詳細な実装の変更を不要に他の機能へと影響させずに済ませることが容易になります。

異なるタイミングで変更されるなら分離する

これは「アジャイルソフトウェア開発の奥義」という書籍(これも現在古本しか無い…)に書かれている「単一責任の原則」に則ったものです。とはいえ、かなりプログラミングしている時の思考では簡略化された形で使っています。

例えば、「一定間隔おきにDBからデータを取得してリストを更新する」という機能の実装の場合、「一定間隔おきに」という部分と「DBからデータを取得する」という部分、そして「リストを更新する」という部分は少なくとも実装を分離するようにと考えます。これは、「一定間隔おき」というのが「情報が変更されるタイミングで更新する」になった場合に残りの機能に触れることなく実装を変更できるようになるからです。これにより、変更する必要のない箇所の詳細な実装を理解する必要もなく、また不用意に実装を変更してしまうことも減ります。他の人が触る際も、このような分離ができていると、機能の実装を理解しやすくでき、また変更が用意になります。

同じタイミングでの変更を最小化する

これも「アジャイルソフトウェア開発の奥義」という書籍に書かれている「単一責任の原則」に則ったものです(と私は思っています)。

同じタイミングで変更しなければならないものが複数の箇所にあると、書き換え忘れといったことがよく発生するようになります。例えば「このメソッドを呼ぶなら、もう1つのメソッドも呼び出し側が常に気を使って呼び出ししないと整合性が保てない」といったことがあげられます。

余談ですが、バージョン管理をしていてコンフリクトが発生した時はこの原則から外れていないか見直すチャンスだと思っています。

繰り返しの排除をする

この原則は「同じタイミングでの変更を最小化する」を達成しようとすると必ず必要になるものだと思います。プログラミングを行っている人なら誰しも知っていて、そして守られない、というジレンマのようなものだと思います。

読みやすいコードは繰り返しがなく、再利用しやすいように適切な命名がされた関数・クラスが用意されていることが多いので、これを考えます。ただ実装途中で見つかることも多いので、その都度行っていくようなものになったりもします。

理解しやすい程度に実装を分割する

巨大なクラス、巨大なメソッドは読みにくいことが多いので、ある程度大きくなってきた時は分離を考えます。コードは他の人が読むものでもあり、人間に対して意図を伝えられるコードであることはとても重要だと思います(実装パターンはこのことを基準にしている話が多かったと思います)。

例えば下記のようなif文があったとします。

if (alpha < 0.001f || !active)
{
    // implementation.
}

このようなコードも、より理解しやすくする方法があって

bool isVisible()
{
    return alpha < 0.001f || !active;
}

// 中略

if (isVisible())
{
    // implementation.
}

のように書いてある方が、より意図を理解しやすくなると思い、実装しています。

間違っていることが早期にわかるように実装する

テストコードでわかるようにする

仕事ではあんまり出来ていないところなので悩みの種であったりもするのですが、プライベートでは基本的にユニットテストのコードは書くようにしています。できればユニットテスト以外も挑戦していきたいところです。

コンパイル時にわかるようにする

これは静的型付き言語にしか当てはまらないことですが、逆にこれに気を使わないのに静的型付き言語を使うのは意味がないとも思います。これを行うことで必要以上に複雑になることは避けたいとは思っていますが、他の人が意図しない利用をしようとした時には、出来る限りコンパイル時に判明するように実装するように考えています。

変更より拡張で対応できるように変更する

アドホックな変更をどんどん加える形で機能追加を行っていくと、大体が理解しにくく、複雑で壊れやすいコードになるので、元のコードに変更を加えずとも拡張できるような構造を取り入れることで対応できるようにします。

例えば、Template Methodパターンなどを用いるなどを行います。

おわりに

原則を書き出すと大体プログラミング時に考えていることのすべてになったような気がします。

もちろんその時々でパフォーマンスや、デザインパターンなど、いろんなことを考えているとは思いますが、主に考えていることは書きだしたことになると思います。

思い出したり、こういうことも考えているなどあれば追記したいと思います。 ソフトウェアテストとか、契約による設計とかを、もっと深く考えながらできるといいんですが、まだまだですね。。。 もっと自分の観点を増やして、よりよい実装や設計ができればと考えているので、今回書きだしたことをこれから見なおして、改善できるところから改善していこうと思います。

それでは!