良いコ-ドへの道―普通のプログラマのためのステップアップガイド

最終回 配列/コレクションを利用した抽象化―その2 Step1:ベタなコードで書いてみる

今回のお題

今回のお題は画像ファイルの一覧を表示するWebアプリケーションです。仕様は次のとおりです。

仕様

  • ユーザは、FTPソフトを使用して画像ファイルをサーバ上の画像ディレクトリに直接アップロードする。画像ディレクトリの場所はWebアプリケーション直下の「webapp/images」注4
  • Webアプリケーションは、画像ディレクトリにアップロードされているファイルの一覧を表示する図1
  • 画像ディレクトリは、⁠amimal(動物⁠⁠food(食べ物⁠⁠landscape(風景⁠⁠」の3つのカテゴリに分けられている図2
  • 画像ディレクトリごとに、ファイルサイズの合計を表示する(図1
  • 画像ディレクトリごとに、ラベルとしてディレクトリの名称を表示する(図1
図1 画像ファイル一覧ページ
図1 画像ファイル一覧ページ
図2 画像のディレクトリ構成
図2 画像のディレクトリ構成

たいへんシンプルな仕様のWebアプリケーションですね。では、この仕様を満たすアプリケーションをいつもの3人組に書いてもらいましょう。

Step1:ベタなコードで書いてみる

まずはベタに実装してみるのじゃ。


かしこ、かしこまりました、かしこ[5]⁠。けど、このくらいの簡単なアプリならいきなり抽象化して考えてもよくないっすかね?

ばかもの! 筆者が困るじゃろ?「 まずはベタに書く⁠⁠→⁠それをリファクタリングしてい く過程を楽しむ」がこの連載のパターンじゃぞ。というわけでベタに書いてみるんじゃ。

画像ディレクトリが3つありますが、ベタに書くとリスト1のような感じになりますね。特に「ベタ」と強調して言わなくても、私はこういうベタなコードしか浮かびませんでした(>_<) メインの処理はstep1メソッドです⁠。3つの画像ディレクトリがあるので、それぞれのディレクトリのファイルの一覧とファイルサイズの合計を計算して、フィールド変数として画面に渡す形にしました。ビューはJSPでリスト2のように作成し、フィールド変数のファイル一覧とファイルサイズの合計を3ディレクトリ分ベタに出力しています。

リスト1 Step1Action
@Path("/")
public class Step1Action extends Action {

    public ServletContext context;
    public File[] foodFiles;
    public File[] animalFiles;
    public File[] landscapeFiles;
    public long foodSize;
    public long animalSize;
    public long landscapeSize;

    public ActionResult step1() {                               ┓
        foodFiles =                                             |
            new File(context.getRealPath("/images/food")).      |
            listFiles();                                        |
        animalFiles =                                           |
            new File(context.getRealPath("/images/animal")).    |
            listFiles();                                        |
        landscapeFiles =                                        |
            new File(context.getRealPath("/images/landscape")). |
            listFiles();                                        |
        foodSize = sizeOfFiles(foodFiles);                      |
        animalSize = sizeOfFiles(animalFiles);                  |
        landscapeSize = sizeOfFiles(landscapeFiles);            |
        return new Forward("step1.jsp");                        |
    }                                                           ┛①

    public long sizeOfFiles(File[] files) {
        long totalSize = 0;
        for (File file : files) {
            totalSize += file.length();
        }
        return totalSize;
    }
}
リスト2 step1.jsp
<h1>Good Code Way 6 - Step1</h1>
<h2>
  Food Photos
  (<fmt:formatNumber
      pattern="0" value="${foodSize / 1024}"/>KB)
</h2>
<ul>
<c:forEach var="file" items="${foodFiles}">
<li>${file.name}</li>
</c:forEach>
</ul>
<h2>
  Animal Photos
  (<fmt:formatNumber
      pattern="0" value="${animalSize / 1024}"/>KB)
</h2>
<ul>
<c:forEach var="file" items="${animalFiles}">
<li>${file.name}</li>
</c:forEach>
</ul>
<h2>
  Landscape Photos
  (<fmt:formatNumber
      pattern="0" value="${landscapeSize / 1024}"/>KB)
</h2>
<ul>
<c:forEach var="file" items="${landscapeFiles}">
<li>${file.name}</li>
</c:forEach>
</ul>
</body>
</html>

なんかコードの重複が多い気がするなあ。DRY[6]に反してない?


そうじゃのう。まずここで重複していそうなコードはcontext.getRealPath(" パス").listFiles()の部分じゃな。このコードは何をやっている部分かな?

対象の画像の一覧を取得してます。


そうじゃ、ここで「対象の画像の一覧を取得する」という処理が重複しているのが気になるのぉ。たとえば「JPEG画像のみ」表示するようになった場合、3つとも書き換えないといけなくなるじゃろ。単純に「コードの重複が悪」なのではなく、意味をきちんと考慮していない「処理の重複」が悪なのじゃ。

今のところ、⁠JEPG画像のみ」という仕様ではないので、そこまでやっちゃうとYAGNI注7に反しませんか!?

そうじゃな。先読みし過ぎはよくないわい。しかし、この部分は「対象の画像の一覧を取得する」処理だということが、ぱっと見てわかりにくいとは思わんか?

そうですね、特にcontext.getRealPathっていうメソッド[8]を今回初めて知ったので、そっちに気が取られてコードの流れがわかりにくくなったように思えて気になっていました……。

メソッドに切り出すことで処理のブロックに「メソッド名」「名前(コードの意図⁠注9を付けることができるので、コメントいらずの可読性の良いコードになることがあるぞ。普通のプログラマよ、ちょっとやってみてくれぬか。

おすすめ記事

記事・ニュース一覧