プログラミングできる気になった自称中級者は、ソースコードに共通のパターンが現れると決まって、その処理を関数などに共通化したがる。
たしかに、そうすることでソースコードは短くなるし、一見して保守性が上がったような気になるのだが、それは間違った作法だから止めろ。
細かいこと言っても伝わらない自称プログラマが読んでることを想定して、先に結論を簡単に書いておく。
なぜコードを共通化するのがいけないのか。理由は簡単だ。要するに、コードが似ているのは単なる偶然であって、それらは別の処理だからだ。
別の処理だから共通化するのはおかしいし、もし共通化した処理の一方のみ仕様が変わった場合、その修正は他方にも影響してしまう。つまり、保守性が下がっている。
たとえば、同じプロジェクトの中に、10%の消費税を加える処理と、10%の金利を加える処理があったとする。この2つの処理はともに元の金額を1.1倍する処理であり、全く同じ処理であるが、共通化してはいけない。
これらを共通化してしまうと、たとえば金利が8%に変更になったとき、金利計算の処理だけではなく、消費税を計算している箇所すべてを変更しなければならなくなる。
実際のアプリケーションでやりがちなのは、複数の処理の「事前処理」「事後処理」などを1つの関数にして、呼び出し毎に細かい挙動を引数で制御するようなパターンだ。
これは結局、改修を重ねる度に「事前処理」「事後処理」の内容が使用箇所によって全く異なるものとなり、それに対応するために
他にも、GUIアプリでユーザーの応答を待つDialogクラスなんてものを作って、使用箇所ごとにメッセージやボタンに割り当てる処理などを切り替えることがある。
これも間違いなく、プログラムが成長するにつれて破綻する。たとえば、ある場所のダイアログは、表示するメッセージがテキスト形式のみではなくなり、脇に画像を表示するかどうかのフラグをコンストラクタに渡したり、Dialogを継承させて表組みを表示するTableDialogサブクラスを作ったりすることになる。ボタンが「OK」と「キャンセル」の2種類の場合じゃなくなって、表示するボタンの数をコンストラクタに渡したり、ボタンに割り当てる処理をリスト形式で渡したりし出す。
こうして、最初は良い設計に見えたDialogクラスはどんどん複雑になる。こうなった原因は明らかで、本来は異なるものを共通化したからだ。おかしな色気を出さずに、素直に別々に実装しておけばよかったのである。
プログラミングをする上で「コードを共通化する」なんてことは意識しなくていい。それよりもプログラマがすべきことは、処理に適切な名前をつけることだ。そのプログラムにおいて「単なる変数の操作」を超えた意味のある処理には名前をつけろ。そして、同じ意味の処理なら同じ関数を使うし、違う処理なら違う関数を使う。それだけだ。コードが共通化できるかどうかなんて全く関係ない。
変数、関数、クラス、名前空間等が再利用のための機構だという先入観は一旦捨てろ。それらの真の意義は、「関心の分離」にある。つまり、実装を隠蔽し、その意図を抽象するために存在する。たまに勘違いしてる奴がいるが、別に1回しか使われない関数とか、1行しかない関数はあってもいい。というか、この原則にしたがって設計すると、ほとんどの関数(or メソッド)は数行になる。
上の消費税の例で言えば、「消費税を加える」「金利を加える」処理は、明らかに単なる算術演算以上の意味のある操作だから、関数化する。そして、それぞれの実装は当初の仕様では奇しくも全く同じになる。消費税を加える箇所では前者の関数を呼ぶし、金利を加える箇所では後者の関数を呼ぶ。
これはこう言い換えることもできる。消費税を加える関数を変更するのは、消費税の計算処理が変わったときのみであり、金利を加える関数を変更するのは、金利の計算処理が変わったときのみである。つまり、すべての関数は、それを変更する理由がただ1つになるように設計しろということだ。
こういうアプローチでプログラムを書くと、ソースコードはあたかもそのアプリケーションのドメイン特化言語で書かれたかのような見た目になる。
また、一つ一つの関数は小さく、理解しやすく、テストやデバッグも容易になる。そして、結果として再利用もしやすくなるし、プログラムの変更も容易になる。
へー、そうなんだ。
意外とちゃんとした説明でしっくりきた
本当に共通化すべきなのは、もっと小さな単位の処理ということでは。
タイトル詐欺乙。共通化を悪いみたいにいうな。 つか、ほぼクラス設計の話をしない時点でおかしい。 なんで関数単位での話しかないの? 継承 と 移譲 の分別、依存の方向などの話を...
プログラミングの話題って、こういう経験もないのに妄想でいっちょ噛みしたがる自称プログラマがかならず沸くよね
共通化が嫌ならアセンブラでも使えば?