文系のためにプログラムの厄介さや設計の仕方を"文"で表してみる

こんにちは、やまだたいし( やまだ たいし (@OrotiYamatano) | Twitter )です。
つくづくプログラムは読んでわかるように書かなきゃならないということを痛感しているのですが、
それには文系的なセンスが必要だと思っているので、それを言語化しようと思ったので本記事を書くことにしました。

目次


なぜプログラムはカオス化するのか


私は大体プログラムコードがカオス化するのは
最初に書いたコードが最初から悪いのではなく、
変更が重なりコードを付け加えられ続けた時、
つまり、思想がごちゃまぜになった時にカオス化すると思っています。

プログラムがカオス化するとは……?


このブログでは意味が理解しづらいコードの状態をカオス(混沌)化と呼んでいこうと思っています。
プログラムの変更をするとき、既存の意味合いを変えず、出来るだけ元のものを変更せずに書き加えることで修正が入ることが多いです。
なぜ既存の部分を変更しないかというと、変更後に処理の内容が変わってしまう危険性が伴うからです。
もちろん、動作を検証するためのテストなどもあったりするのですが、
まだ不完全で私が所属するゲーム業界では処理に時間も関係してくるため、まだ有用なテスト方法がなかったりします。

ちなみに世間一般的には、このような理解しづらくなってしまったコードを「レガシーコード」、「スパゲッティコード」、「ダーティーコード」などと呼んだりします。

カオス化する流れを文に例えて言い表してみる


例えば、

やまだたいしは目玉焼きにマヨネーズをかける

という文章をプログラムのコードに見立てた場合。

「あ、やっぱり、塩コショウ使うときもあるな」と気付き修正が加えられた場合は

やまだたいしは目玉焼きにマヨネーズをかけるが、塩コショウを使うこともある

という文章に内容が変わります。
先程も言ったようにプログラムは「出来るだけ元のものを変更せずに書き加えることで修正が入る」ことが多いためです。

これぐらいならまだ許せます。
しかし、「周りに合わせて醤油かけるときもあるな」と更に気づいた場合、

やまだたいしは目玉焼きにマヨネーズをかけるが、塩コショウを使うこともあり、周りに合わせて醤油を使うこともある

というように変更が加えられる場合が多いです。
こうなってしまうと中身の理解がしづらくなってしまいます。

更に「連続して同じ調味料は使わない」というような条件が入ったらどうなるでしょうか。

やまだたいしは目玉焼きにマヨネーズをかけるが、塩コショウを使うこともあり、周りに合わせて醤油を使うこともある。連続して同じ調味料を使うことはない

このような文章になってしまうと、もう何が言いたいのかわからないと思います。
はい。適当な変更が加わった結果、プログラム自体の意味を曖昧にし、理解しづらいものに変化させてしまうのだと皆さんお気づきだと思います。
つまり、プログラムが最初意図されたものと異なる状態になってしまうことが、いわゆるカオス化(スパゲッティコード、レガシーコード)と呼ばれる状態を引き起こしてしまうのだと私は考えます。

では、「既存の文章にも手を加え書き換えたら良いじゃないか」という意見があると思います。
私もそう思います。意味はそのままに書き換えること、人はソレをリファクタリング(リファクタ)と呼びます。

例えば、上の文章をリファクタすると以下のような文になります。

やまだたいしは目玉焼きを食べる時につける調味料はマヨネーズ、塩コショウ、醤油それぞれを状況に応じて使い分ける。調味料は連続して同じものは使わないが、気分によって変えることがあるし、周りが目玉焼きを食べているときは周りに合わせることもある。

スッキリしていますね。コレがコードが最適化された状態です。
最初から文章に必要な要素が分かっていた場合はこのような内容(文)で書かれる(実装)されると思います。
しかし、更に変更が加える必要が出た場合は文章全体を修正する必要が出てきます。
この状態をプログラミングでは結合度が高いと言います。

結合度が高くとも変更がなければ構わないのですが、
綿密に必要な要素を調べ設計しても最初から全ての要素が見えた状態でスタートが出来ない場合が多いです。
また、大規模案件ほど既存の出来上がってるものに対して手を加えるのが許されない場合が多いです。
では、どうすればいいでしょうか。

抽象的に組み立てる


プログラミングでは抽象的に組み立てることで、後からの変更を許容できるように作ることが多いです。
俗に言われるInterfaceや継承と呼ばれるものを利用します。

例として上であげた文章↓を抽象的に組み立てた場合どうなるでしょうか?

やまだたいしは目玉焼きにマヨネーズをかける

抽象化した場合↓

やまだたいしは目玉焼きを以下の調味料で食べる
・マヨネーズ

のようになります。
プログラムで言い表すと食事クラスから、食べ方クラスとして分離して、Interfaceとして実装し食べ方クラスを継承したマヨネーズクラスを定義したといえば良いでしょうか。

こうかけるなら、いくら調味料が増えても既存の文章を書き換えること無く調味料を追加することが出来ます。

やまだたいしは目玉焼きを以下の調味料で食べる
・マヨネーズ
・気分によって塩コショウ
・周りに合わせて醤油

このように書き換えることを「関心の分離をした」と言ったりします。
「じゃあ、全部抽象化してしまえば、プログラムにいくらでも変更が加えられていいじゃないか!」と思う方がいるかも知れませんが、それはソレで「凝集性」が失われてしまうため危険です。

分離されすぎたコード


抽象化されすぎたコードがどのように危険か具体的にピンと来ない方もいると思うのでここでも文で言い表してみます。
また↓の文章を抽象化し過ぎた例を紹介しようと思います。

やまだたいしは目玉焼きにマヨネーズをかける

この文章を抽象化し過ぎた場合は以下のようになります。

1の人物は2の食べ物を3の調味料で食べる
1→やまだたいし
2→目玉焼き
3→マヨネーズ

どうでしょうか?
複雑性が増し上と下の文章を見比べる回数が増え、理解に時間がかかってしまい冗長な内容になってしまったように感じませんでしょうか?
コレをプログラミングでは凝集性が下がった状態と呼びます。

つまり、プログラミングで抽象した実装をする場合、
最初から「だいたいこの辺が変更が掛かりそうだ」と理解している箇所を抽象的にすることでコードの変更を少なくすることが可能になってくるということです。
変更がかからない箇所を不用意に抽象化してしまうのはあまりいいことではありません。

適切な変更手順


とはいえ最初から変更箇所は予測できません。
では、どのようにクラスに対して変更をかけていけば良いでしょうか?
私はこのような順番で変更がかけられると良いと考えます。

やまだたいしは目玉焼きにマヨネーズをかける

やまだたいしは目玉焼きにマヨネーズをかけるが、塩コショウを使うこともある

↓ この時点で「まだ増えそうかも?」と予測することができるので抽象度の高い書き方に書き変えられます。

やまだたいしは目玉焼きを以下の調味料で食べる
・マヨネーズ
・塩コショウ

やまだたいしは目玉焼きを以下の調味料で食べる
・マヨネーズ
・気分によって塩コショウ
・周りに合わせて醤油

というふうに順序を追って変えられると良いなと私は考えます。

つまり定期的に構造を見直し、リファクタ、クラス設計の変更を行っていくことが大事。
考えなしに内容を変更し続けてしまうと、既存の内容が理解できない状態になってしまい、プログラムも変更できない状態になってしまいます。
エンジニアからリファクタしたいと要望が入ったときにはもう既に遅く、既に理解しづらい状態になっていると思われます。
そもそもエンジニアは日々リファクタをできる状態なのが適切であり、
理解できる状態のうちにプログラムをリファクタしクラス設計を変えることが大事だと私は考えます。

リファクタをしたいという要望が入るということはプログラムがカオス化している&日常的に書き換えが許されていないということなので、改めたほうが良いと思います。
もし、エンジニアからリファクタしたいと要望が入ってしまった場合は「運用をしないプロジェクト」かつ「プロジェクト後期」なら、もうリファクタを諦めて作りきってしまうことをオススメします。

ちなみにゲームはレイヤードアーキテクチャを採用しないことが多いですが、
ウェブのようにソレ以外に作りようがないという場合が少ないため
そのような実装になってしまいます。

もしレイヤードアーキテクチャを採用するならUIなどの部分が一部採用できるかなと思います。

プログラムを変更することの大変さ


文系の方に設計などを理解してもらうため書いてみましたが、コレで大変さが少しは理解してもらえたのなら嬉しいです。
エンジニアの方に対して、機能変更として変更してほしいという場合は、一行だけしか変えてないのに凄い時間がかかる場合があります。
それは文章でいうと、小説の中で前後関係を全て理解した上で文章に違和感ない一文を追加するようなもので、
いわゆる伏線など不整合が起こらないように読み返したり、適切な文章を考えたりしているため時間がかかってしまう状態なのです。

抽象度の高い実装をしている箇所なら、書き加えるだけで済む場合もありますが、
そうではない場合、大規模な変更を強いられることもあります。

この文章を見ているアナタがクライアントなら少し多めにみてやって欲しいと私は思います。

まとめ


思いつきで文でプログラムを表すことが出来るのではないかと思い書いた内容でしたが、案外書き表すことが出来驚きました。
プログラミングは数学的要素を問われるため理数系と捉えられがちですが、
今ある既存の内容を言語化し理論整然とした内容に書き換え文章化するという意味では、かなり文系要素が多いと私は考えます。
理数系だからプログラムが得意だとか文系だからプログラムが苦手とかそういうのはないと個人的には思います。

このブログが少しでも文系エンジニア(プログラマ)の助けになれば良いなと思います。
以上、やまだたいしでした。