仕事で他人のソースとかみていて、結構関数の分け方をわかっていない人が多いと思いました。
一つの関数の行数が1000行を超えているようなソースを書く人をたまに見かけます。
そういったソースは、初見ではかなりの確率で間違った解釈をしてしまいますし、正直作った本人にしか(しかも作った瞬間だけ)分かりません。
これから、私のSE生活でこんなソースをあまり見たくないので、なにか考えるヒントになるようなものを書けたらなと思います。
※あくまでも私の主観なのでこの考えに賛同できない方もいると思います。まぁこんなこと考えている人もいるんだなー程度に見ていただけたらなと思います。
#なぜ関数が長くなるのか
一つの関数がなぜ長くなるのか。それには、2つ理由があると思います。
-
同じ処理を何回も書いている
同じ処理を何回も書いていたら当然、その分無駄に処理が長くなります。
こういう時にこそ、普通に関数を利用すればよいと思います。ただし、既存ソースに手を加えれない場合は仕方ないと思います。 -
単純にやらないといけないことが多い
こういう場合は、ある程度長くないのは仕方ないと思います。
それでも、意味の区切り、目的と手段をはっきりさせることでより分かりやすく記述できるはずです。
関数に分けるということを考えずに作ってしまうケースが多いと思います。
#行数が長い関数のデメリット
どんなデメリットがあるのか。
期待通りに動くんだから良いやんって考える人もいるかもしれませんが、ぜひ見直してほしいです。
-
可読性
行数が長い関数には、いろんな処理が詰まっています。そのため、関数の目的がじっくり読まないとほとんど伝わりません。
また、解読しはじめは関数の目的を分かっていても少し複雑な処理が出てきたら、目的を忘れやすくなります。 -
修正に時間がかかる
1番と関係がありますが、理解しにくいので処理の原因を探すのに時間がかかります。
また、関数内にはローカル変数を定義することが多いですが、そのローカル変数のスコープが大きすぎてプチグローバル変数のようになりより処理を複雑にしてしまう可能性があります。
結局、影響調査にも時間がかかるし良いことがありません。
#関数の長さを適度にすることのメリット
長さを適度にすることでどのようなメリットがあるのか説明していきたいと思います。
正直にいって関数がなくてもプログラムは書けます。(javaはmain関数が必要ですが)それなのに、なぜ関数という機能が生まれたのか。
理由を考えるとわかると思います。
-
理解のしやすさ
関数を利用することで処理の理解のしやすさが格段に上がります。
たとえば、100行の文章(twitter等)を書くときには、小題とかは書かないと思います。
これは、小題を書かなくても内容が短く相手に内容が伝わるからです。そのような場合は、もちろん小題はいりません。
反対に、このようなブログのように何千文字を超えるような文章を書くときは、章や小題を書くと思います。
小題を書くことによって、相手に何を伝えたいのか分かりやすく意味の区切りを伝えることができます。
また、読み直したい時も必要な個所をすぐに特定することができます。
プログラミングでいう関数もこの小題と同じようなものです。関数で区切ることにより、やりたいことがより一層理解しやすくなります。 -
再利用が容易
関数として処理を切出すことによって、INとOUTさえ理解すれば中身を理解する必要がなくなり再利用もしやすくなります。
たとえば、ネットで乗り換え検索すると、出発地と到着地を指定すれば道順を示してくれます。
本来、~線は何分発の電車がある。急行ならもっと早く行ける等の情報を知っていないとできないことが出発地と到着地さえ分かれば教えてくれます。
これは、乗り換え検索関数を準備しているのと同じようなものです。
この関数により私たちは、詳細な情報を毎回調べなくても乗り換え検索ができいつでも利用できるのです。
#どうすればよいのか
-
外部ライブラリの使用を検討
ありきたりな話ですが、まずは、外部ライブラリを使用することを考えてほしいです。
ライブラリには、業務ロジックに関係のない共通処理を簡潔に表現できるような関数がたくさん含まれているものがあります。
それを使用するだけで簡潔にわかりやすく表現することができます。javaの「apatch common」とかよく使用する処理を非常に簡単に実現できるようになっています。
使用するとしないで、実装にかかる時間も変わりますのでぜひ使用しましょう。
自分で書ければ良いですが、時間もかかりますので外部ライブラリをおススメします。 -
処理の目的を考える
1つの処理は、何か目的があって実施されるものです。
その処理の中で、目的を達成するための手段のための処理と目的のための処理に切り分けてください。
そして、切り分けた手段のための処理を関数として切り出すと読みやすい書き方になります。
例えば、検索処理という関数があるとします。
その処理を切り分けるとしたらこのようになります。
正直検索条件の数が少なければ、この内容をすべて一つの関数に記述しても大して問題ないと思います。
けど、今回はめっちゃ検索条件の数も多いし、入力チェックも多いとします。
検索処理の内容を切り分けるとこのように、「入力チェック」「SQL組み立て」「検索処理」の三つに分けれます。
今回の関数のメイン処理は検索です。そのため、それ以外の処理は、別関数にしてしまうのがベストです。
入力チェック関数、SQL組み立て関数(基本sqlファイルを作成すると思うので必要ないが。。)を別途作成して検索処理から呼び出すようにしましょう!
こんな感じです。
private Dao dao;
public void search(final Bean bean) {
// 入力チェック処理
String arg = bean.getArg();
if ("1".equals(arg)) {
throw new CheckException();
}
// sql組み立て
~
// 検索
dao.search(bean);
}
private Dao dao;
public void search(final Bean bean) {
// 入力チェック処理
checkSearchInput(bean);
// sql組み立て
buildSql(bean);
// 検索
dao.search(bean);
}
private void checkSearchInput(final Bean bean) throw CheckException{
String arg = bean.getArg();
if ("1".equals(arg)) {
throw new CheckException();
}
}
private void buildSql(final Bean bean){
~
}
このようにすることによって、一つ一つの関数の目的がはっきりしてどこで何をしているのか分かるようになりますね。
下記図のように考えられるとベストだと思います。
この図の中で手段をそれぞれ関数にしていくイメージです。手段の手段は関数の長さ次第で適時関数にするかどうか決めてください。
僕の関数に対する思いをこれで終わります。
皆さんの考え方とかいろいろ教えてください。