Developer's Blog

ソースコードは口ほどにものを言う

初めまして、こんにちは。ウェブ開発担当の古賀です。

突然ですが、プログラマの実装力がもっとも顕著に表れるシーンはどのような時だと思いますか?

様々な回答があると思いますが、私が個人的に重要視しているのは、ゼロから実装ができるか?という点です。

ゼロからというのは、要件とか設計とかそういうものがないのではなくて、 ソースコードをまだ1行も書いていない、実装に関する規約やルールもない、 使用するフレームワークやライブラリの選定もしていない状態から、 実装を進めていけるのか?ということです。

こういう状態で作り始めた場合、その人の個性や性格が顕著に表れるため、作成されたソースコードを見てみると大変興味深いと感じます。

所謂「宗教論争」と言われる実装手法の選択の違いも、その人の個性や性格が透けて見えるものです。

前置きが長くなりましたが、今回は私の独断と偏見で、3 つのソースコードの癖から性格分析をしてみようと思います。

観点1:括弧をつけるかつけないか?

選択肢1:省略可能な括弧なので、付ける必要はない

if (exists)
    echo 'exists は true です';

楽観的で、後から処理を追加する時に括弧を付け忘れるリスクは特に気にならない。

選択肢2:複数行処理がある場合と全く同じフォーマットで実装する

if (exists) {
    echo 'exists は true です';
}

慎重派でリスクを嫌う。また、美的感覚的に、全体のフォーマットの統一感が取れているものこそ美しいと感じている。

選択肢3:括弧はつけるが、改行をしない

if (exists)  { echo 'exists は true です';  }
if (exists2) { echo 'exists2 は true です'; }

括弧を省略するのはリスクが高いが、複数行処理がある場合とのフォーマットの統一感よりも、 合理性を優先したい。
処理が追加になる時に改行すれば何も問題なく、全体の統一感よりも制御構文が1行で書ききれることの方に美しさを感じている。

観点2:コメントに何を書くか?

選択肢1:そもそもコメントなんて不要

function funcA() {
    for (i = 0; i < 3; i += 1) {
        initLib();
        executeLib();
    }
}

コメントが必要なソースコードは書きたくない意識が強くあり、余程のことがない限り、コメントを書かない。

選択肢2:ソースコードからは読み取れない「何故」の部分を書く

function funcA() {
    // ライブラリの制限のため、必ず 3 回ループする
    for (i = 0; i < 3; i += 1) {
        initLib();
        executeLib();
    }
}

ソースコードからは「何故」が読み取れないことを知っており、「何故」が後世の人にとって必要になる可能性が多くあることを理解している。また、「何故」の部分を明文化するため、論理的で、多面的に物事を見る力がある人が多い。

選択肢3:初心者が読んでも大丈夫!多めに説明を書く

function funcA() {
    // ライブラリの制限のため、必ず 3 回ループする
    for (i = 0; i < 3; i += 1) {
        // ライブラリ初期化処理
        initLib();
        // ライブラリ実行処理
        executeLib();
    }
}

基本的には慎重派に分類できるが、その背景によって、更に 2 つのタイプに分類できる。

1 つ目は、自己評価が低いが故に慎重になっているタイプ。自分のソースコードが他者から理解されるか自信が無いため、本来不要なはずの事柄までコメントに書いてしまう。

2 つ目は、トリッキーな実装になっているコトを自覚しており、かなりきっちり、長いコメントを書く。

慎重が故に 多めにコメントを書いてしまい、やや冗長になることもある。

観点3:例外捕捉のタイミング

例外の捕捉タイミングは、オブジェクト指向の熟練度にも直結する実装の特徴ですが、やはり性格も表れやすいところかと思います。

選択肢1:とりあえず、最上位で Exception クラスのみ捕捉

public class ClassA {
    public void ExecuteFunction() {
        try {
            this.FunctionA();
        } catch(Exception e) {
            // ・・・(中略)例外発生時処理・・・
        }
    }

    private void FunctionA() {
        // ・・・(中略)・・・
    }
}

楽観的で、面倒くさがり。「とりあえず捕捉し損ねなくて一番安全でしょ」と思っている。

選択肢2:同じく最上位で、複数の catch 句で捕捉

public class ClassA {
    public void ExecuteFunction() {
        try {
            this.FunctionA();
        } catch(IOException e) {
            // ・・・(中略)ファイル読み書き系例外発生時処理・・・
        } catch(Exception e) {
            // ・・・(中略)想定外の例外発生時処理・・・
        }
    }

    private void FunctionA() {
        // ・・・(中略)・・・
    }
}

最終的に捕捉していればスタックトレースで辿れるからOK。
でも一応、想定内の例外と想定外の例外は違うハンドリングをしておこう。という合理的なタイプ。
途中で変に捕捉して握りつぶしてしまうよりはイイと考える。

選択肢3:各メソッド単位で例外を捕捉し、上位にも伝播すべきか、その場で処理をすべきかを判断する

public class ClassA {
    public void ExecuteFunction() {
        try {
            this.FunctionA();
        } catch(IOException e) {
            // ・・・(中略)ファイル読み書き系例外発生時処理・・・
        } catch(Exception e) {
            // ・・・(中略)想定外の例外発生時処理・・・
        }
    }

    private void FunctionA() {
        try {
            // ・・・(中略)・・・
        } catch(FileNotFoundException e) {
            // ・・・(中略)ファイルが存在しない場合の例外のみ、
            // このメソッド内で適切にエラー処理して握りつぶす・・・
        }
    }
}

慎重派で、本当に上位で捕捉すべきか、そのメソッド内で解決できるのか熟考して実装しているタイプ

まとめ

このように、ゼロから実装したソースコードというのは自由な分だけ如何様にも作れるので、自分で作ったソースコードでも「ここまで慎重派だったのか」とか「こういう時は大胆になれるのか」とか気付きがたくさんあります。

人のソースの場合は、その人がどのように思考する人で、どういうところに繊細な面を持っているのかなどが垣間見れて、その後のコミュニケーションの参考になるものだと感じます。

一度、そのような観点でソースをお互いに見合うというのも面白いかもしれませんね。

 

フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!

 

Copyright © 2019 Fenrir Inc. All rights reserved.