新人がJava開発中読みやすいコードを書くために注意すべきこと


はじめに

大学も院生も文系の私は、会社に入ってはじめてJavaに携わりました。
新人研修を経て、Java開発経験は一年半しかない私が、今まで参加したプロジェクトの中にソースコードを書く時注意すべき点、またレビューで指摘された点を共有したいと思います。

クラス名、メソッド名、変数名を分かりやすいようにする

① 既存ソースコードの命名規則に従う

この一年半に携わった案件は、ほぼ既存システムの新規機能作成、もしくはバッチ作成です。
新しいクラスを作成する前に、まずは既存ソースコードを読んで、命名ルールを把握することが大事です。
とりあえず、着手する前に、既存のコードを読んでください!

例:
 OldProductDeleteBatch.java ⇒既存
 CampaignFileStopBatch.java ⇒新規

名詞+動詞+Batchという命名ルールです。

② 変数名は長くても理解しやすいようにする

error とかmessage とかid どいう変数名は短いですが、よくよく考えたら何の情報も伝わりませんね。
なんのエラー?どんなメッセージ?どこのid?と、書いた人以外誰にも分からないです。

もっと具体的に、
フォーマット表示エラーならformatError
ワーニングメッセージならwarningMessage
ファイルIDならfileId
と書くようにしましょう。

③ 正しい英語を書くよう心がける

英語が苦手な人はもちろんいますが、ソースコードを読む人に意図を正しく伝えるため、正しい英語が必要だと思います。

例えば:
① ファイルが存在するかを判定するboolean変数
boolean fileExist ⇒boolean fileExists

② キャンペーンファイルIDを格納する変数
String campignFileId ⇒ String campaignFileId

コメントを積極的に書こう

ベテランのエンジニアにとって、こまめにコメントを書かない方がいいかもしれないが、初心者のうちに積極的に書いた方がいいと思います。

初めのうちは、下記のように処理内容をコメントで羅列し、全体像を掴んだ上でコードを記述するようにしていました。
こうやって何をすべきかも整理できるではないでしょうか。

comment.java
/*--------------------------------------------------------------------------------------------------------------
アップロードしたTSVファイルの各行を調査する
    0.id        1.名前    2.学籍番号

    ※ TSVファイルに記載された学籍番号はDBの番号int_number

    エラーが発生した場合、以下のメソッドを呼び出して、エラーメッセージを一行ずつシートに入れる
    ・addErrorRow():すべてのデータがnullの場合
    ・addStudentErrorRow():学籍情報がエラーの場合
--------------------------------------------------------------------------------------------------------------*/
for(){
    //具体の処理を書く
}

そしてもう一つ、時間をたつと自分が何を書いたかを忘れたことはエンジニアとしてよくあります。
この場合にコメントがあればソースコードの内容をすぐ理解できると思います。

似たような処理をまとめて書く

いくつかのバッチ処理を作成している間に、似たような処理を書いたことに気付きました。

例えば以下のExcelシートを作成する処理:

ExcelFormat.java
    public static Workbook newWorkbook(){
        Workbook newWorkbook = new SXSSFWorkbook();

        CellStyle styleHeaderLabel = null;
        CellStyle styleHeaderValue = null;
        CellStyle styleHeaderValue = null;

        DataFormat format = newWorkbook.createDataFormat();

        //fontの設定
        Font font = newWorkbook.createFont();
        font.setFontName("MS ゴシック");
        font.setFontHeightInPoints((short)9);

        //ヘッダ文字列表示用の書式の設定
        styleHeaderLabel = newWorkbook.createCellStyle();
        styleHeaderLabel.setBorderBottom(CellStyle.BORDER_THIN);
        styleHeaderLabel.setBorderTop(CellStyle.BORDER_THIN);
        styleHeaderLabel.setBorderLeft(CellStyle.BORDER_THIN);
        styleHeaderLabel.setBorderRight(CellStyle.BORDER_THIN);
        styleHeaderLabel.setFillForegroundColor(HSSFColor.LIGHT_CORNFLOWER_BLUE.index);
        styleHeaderLabel.setFillPattern(CellStyle.SOLID_FOREGROUND);
        styleHeaderLabel.setVerticalAlignment(CellStyle.VERTICAL_TOP);
        styleHeaderLabel.setWrapText(true);
        styleHeaderLabel.setFont(font);

        addHeader(newWorkbook);

        return newWorkbook;
    }

    public static void addHeader(Workbook workbook){
        //シートを初期化して、シートの名前を設定する
        Sheet mainSheet = workbook.createSheet();
        mainSheet.createFreezePane(0, 1);
        workbook.setSheetName(SHEET_NO, SHEET_NAME);

        int colNumber = 1;
        int rowNumber = 1;
        Row row = mainSheet.createRow(rowNumber);
        Cell cell = null;
        CellStyle styleHeaderLabel = workbook.getCellStyleAt((short)(workbook.getNumCellStyles()-3));

        //ヘッダ-を挿入する
        for(String headerName : HEADER_LIST){
            cell = row.createCell(colNumber++);
            cell.setCellStyle(styleHeaderLabel);
            cell.setCellType(Cell.CELL_TYPE_STRING);
            cell.setCellValue(headerName);
        }

        int maxColNumber = 0;

        //カラムの大きさを調整する
        for(int i = 1; i < colNumber; i++) {
            mainSheet.setColumnWidth(i, 5000);
            maxColNumber = i;
        }

        //ヘッダ行をオートフィルタに設定する
        mainSheet.setAutoFilter(new CellRangeAddress(1,1,1,maxColNumber));
    }

実はバッチ処理の中にこういう似たような処理がけっこうあって、一つのクラスにまとめて処理を共通化するとソースコードが短くなって、可読性も上がります。
自分のソースの中に同じような処理が2か所以上に存在している時、「この処理を共通化できないか」「どこかでまとめられないか」を考えましょう。

独立性が高い書き方にしよう

以下のソースコードを見てみましょう。

file.java
for(int i=0;i<file.arrtibuteSize();i++){
    if("00000000".equals(file.getAttribute(i).getId()) && !file.getAttribute(i).getValue().isEmpty()){
         ids.add(file.getAttribute(i).getValue());

         break;
    }
}

ここの!file.getAttribute(i).getValue().isEmpty()書き方は、file.getAttribute(i).getValue()の値が必ずnullにならないという前提が必要です。(nullが設定されていた場合に NullPointerExceptionが発生しますので)
今回に参照先のオブジェクトにはここの値はnullにならないように設定されたが、設定されていない時の状況も考えなければなりません。

こういう独立した処理で作られるオブジェクトの中に参照する場合に、何か設定されいても落ちないような書き方を心掛けた方が安全です。

修正したもの:

file_2.java
for(int i=0;i<file.attributeSize();i++){
    var fileAttr = file.getAttribute(i);
    if(!"00000000".equals(fileAttr.getId())){
        continue;
    }

    var value = fileAttr.getValue();
    if(value != null && !value.isEmpty()){
          ids.add(value);

          break;
    }
}

こう書くと、NullPointerExceptionを避けるでしょう。

書き方を統一する

とても細かい話ですが、最初にソースコードを書き始める時に、個人的なルールを決めた方がいいと思います。
例:

rule.java```
for(int i =0;i<ids.length;i++) {
    //なにかの処理を書く
}

上記のソースコードは分かり辛いかもしれないですが、forの後の引数を記述する()について、forの後にスペースはあげていませんが、括弧の後にスペースを入れていますね。
こういうforとifの後の引数を表示する()について、半角スペースをあけるかどうかを統一しましょう。

rule2.java
for(int i=0;i<ids.length;i++){
    //なにかの処理を書く
}

まとめ

いかがでしたでしょうか。

最初はソースコードを動かすことだけで精一杯でしたが、少しずつ可読性、作成ルールを考えるように進めています。
私にとって「プログラミングを勉強することが目的ではない」ということは最も注意しなければならないことです。
大事なのは「何かを作りたい」「どんな機能を実現したい」という動機があって、それに応じて調べ、プログラミングすることだと思います。