弥生研究所

人は誰しもが生きることの専門家である

コードの共通化に対して考えること

趣旨

何も考えていないのであれば、まずコードは共通化させずに冗長に書こう。

背景

やめた方が良いコーディング・設計、アンチパターン にて、共通化、抽象化について言及されていた。 内容は非常に納得するものであった。 論旨は、"何も考えずに"共通化、抽象化をしなかったり(あるいはしすぎたり)することが問題である、と私は理解している。 一方で、私の経験では、共通化をしなかったことよりも、結果としてなんであそこを共通化してしまったかな、という後悔のほうが多い気がして、私なりの考えを書くものである。 私の論旨は、前述の通り、まずは難しいことを考えずに、冗長に書いちゃっていいんじゃね、ということである。

通化したい誘惑

適切に共通化された処理はとても綺麗だ。まさにプログラマ冥利に尽きるものである。

でも、時に仕様変更などが繰り返されると、共通化されていた処理が複雑化の温床になることもある。重要ゆえに共通化された処理が、仕様変更によって複雑な条件分岐が盛り込まれ、もはや共通化とは何なのかという状態になり、だれも手を付けたくない代物になることがある。

冗長なソースコードのデメリットは、修正するときに修正範囲が散らばる可能性があるということだ。しかし、見方を変えると、修正範囲は散らばっても、影響範囲の把握は容易である。逆に、ソースコードが共通化されている場合、修正箇所は唯一かもしれない。ところが、共通化されているがゆえに様々なところから呼ばれ、おまけに複雑に条件分岐が絡んでいると、影響範囲の特定が難しくなる。どのみちテストではすべてのパターンでテストしなくてはいけない。影響範囲の特定が難しく、洗い出しに漏れがあると、このパターンではOKだけど、このパターンではNGでしたということが起こり得る。

処理の役割を明確にする

考えてほしいのはクラス、メソッドには必ず目的と役割があるということである。あなたが修正したいと考える内容が、果たしてそのクラス、メソッドの、目的と役割に一致しているかを、常に考えてほしい。あなたが、付け加えようとしているその修正は、メソッドの設計意義を壊すのではないかどうか。また、クラスやメソッドを書く人は、その目的と役割をコメントなりにきちんと表現しなくてはならない。

通化の目的は?

シンプルなものを複雑にするのは簡単だが、複雑なものをシンプルにするのは難しい。処理の共通化は後からいくらでもできる。でも、一度共通化された処理を分解するのは難しい。だから、処理の共通化は下手に行うべきではなく、よくよく考えて慎重に行うべきである。

おそらく、処理を共通化するということ自体が、ある程度の技術と経験を要するものなのだと思う。だからこそ、共通化していない状態がアンチパターンとして上がるわけだ。でも、共通化された処理をメンテするのもやっぱり、技術と経験が必要なのである。メンテする人が必ずしもふさわしい技術と経験を持っていないことがある。そうすると共通化の意義が壊されてしまう。共通化された繊細なコードと、冗長的な堅牢なコードは、どちらのほうがプログラマに優しいのだろうか。

冗長な処理を共通化するのは、複雑に共通化された処理を分解するのに比べたら、はるかに簡単だ。だとしたら、最初は共通化という高い目標は掲げずに、共通化が必要な時が来るまでは、冗長に書いてしまったほうが、技術的負債になりにくい。

通化された処理は仕様変更に脆い

難しいのは、仕様変更というものが、全く予測のつかないものだということだ。設計する段階では、あらかじめ予測しうる仕様変更を想定して、柔軟に対応できるような設計にしようとする。しかし、事前の予測などは軽々と超えてしまうのが仕様変更だと言ってもいい。人間の営みなど、神にとっては児戯に等しいと言わんばかりに、人知では計り知れないのが仕様変更なのである。だとしたら、何が仕様変更に耐えられるのか。仕様変更に耐えられるのはシンプルさだけである。それは冗長さと表現しても良いかもしれない。シンプルなものを複雑にするのは簡単だ。仕様変更によってシンプルなものが複雑になっていくのであれば、最初の段階を最もシンプルにすることが、仕様変更に対して柔軟性と堅牢性を発揮する。