良いコ-ドへの道―普通のプログラマのためのステップアップガイド
最終回 配列/コレクションを利用した抽象化―その2 Step1:ベタなコードで書いてみる
今回のお題
今回のお題は画像ファイルの一覧を表示するWebアプリケーションです。仕様は次のとおりです。
- 仕様
- ユーザは,
FTPソフトを使用して画像ファイルをサーバ上の画像ディレクトリに直接アップロードする。画像ディレクトリの場所はWebアプリケーション直下の 「webapp/ images」 (注4) - Webアプリケーションは,
画像ディレクトリにアップロードされているファイルの一覧を表示する (図1①) - 画像ディレクトリは,
「amimal (動物)」「food (食べ物)」「landscape (風景)」の3つのカテゴリに分けられている (図2) - 画像ディレクトリごとに,
ファイルサイズの合計を表示する (図1②) - 画像ディレクトリごとに,
ラベルとしてディレクトリの名称を表示する (図1③)
- ユーザは,
たいへんシンプルな仕様のWebアプリケーションですね。では,
- 注4)
- Webアプリケーション直下に動的に画像ファイルを配置すると,
アプリケーションの再デプロイ時にデータが消えてしまったり, セキュリティ上の問題 (JSPをアップロードされてしまうなど) が発生したりします。通常, このようなファイルは, 適切にパーミッションが付けられたデータ用のディレクトリ (たとえば 「/data/ images」 など) に置くことが多いと思います。今回はサンプルということでご理解ください。
Step1:ベタなコードで書いてみる
まずはベタに実装してみるのじゃ。
かしこ,
ばかもの! 筆者が困るじゃろ?
画像ディレクトリが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.
<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
そうじゃのう。まずここで重複していそうなコードは
context.
の部分じゃな。このコードは何をやっている部分かな?
対象の画像の一覧を取得してます。
そうじゃ,
今のところ,
そうじゃな。先読みし過ぎはよくないわい。しかし,
そうですね,
メソッドに切り出すことで処理のブロックに
- 注5)
- 意味がわからない人は
「ハイキングウォーキング かしこかしこまりましたかしこ」 で検索してください。 - 注6)
- DRY
(Don't Repeat Yourself, 重複しちゃいけないよ)。システムのあらゆる場面で情報の重複を避けて, 開発効率を上げたり, メンテナンス性を高めることです。書籍 『達人プログラマー』 で紹介されている考え方です。単純な 「コードの重複」 のみを指すものではありませんので, ここでの中級プログラマのDRYの使い方は若干微妙です。単純なコードの重複の排除は 「Once and Only Once」 と呼ばれます。DRYについて詳しくは特集1 「現場で役立つDRYの基礎知識」 (9ページ) を参考にしてください。
参考:「DRY (Don't Repeat Yoursel) 」の意味を勘違いしてたかも (kwatchの日記) - 注7)
- YAGNI
(You Aren't Going to Need It, どうせいらないって)。将来ばかりを考え過ぎても予想は外れることが多いので, 今必要なことだけをシンプルにやっていきましょう, という考え方です。あとで本当にその機能が必要になっても, シンプルに保っておくことで機能が追加しやすいというわけです。
参考:「YAGNI ~ 予想でモノを作るな」 (悪態のプログラマ) - 注8)
- context.
getRealPath。Servlet APIのServletContextのメソッド。このメソッドに 「コンテキストのルートパスからの相対パス」 を渡すと, ファイルの絶対パスを取得できます。サーブレットコンテナの実装によってはnullが返ることがあるので注意が必要です。詳細は次のAPIマニュアルを参照してください。
http://sdc. sun. co. jp/ java/ docs/ j2ee/ sdk_ 1. 3/ ja/ techdocs/ api/ javax/ servlet/ ServletContext.
html#getRealPath(java.lang. String) - 注9)
- 名前付けについては,
本誌Vol. 45 の本連載第2回「名前付け重要。または, 良いコードは良い名前から生まれるんです。 」を参照してください。
バックナンバー
良いコ-ドへの道―普通のプログラマのためのステップアップガイド
- 最終回 配列/コレクションを利用した抽象化―その5 Step4:配列/コレクション化して抽象化する
- 最終回 配列/コレクションを利用した抽象化―その4 Step3:関連のあるデータをオブジェクトに
- 最終回 配列/コレクションを利用した抽象化―その3 Step2:可読性を高めるためのメソッドの抽出
- 最終回 配列/コレクションを利用した抽象化―その2 Step1:ベタなコードで書いてみる
- 最終回 配列/コレクションを利用した抽象化―その1 配列/コレクションって何?
- 第5回 メタプログラミング―Excelを使ったDSLを作ろう―その4 Step3:リフレクションAPIで変換ルールを動的に適用する
- 第5回 メタプログラミング―Excelを使ったDSLを作ろう―その3 Step2:メタ情報をExcelに移動する
- 第5回 メタプログラミング―Excelを使ったDSLを作ろう―その2 Step1:ベタなコードで書いてみる
- 第5回 メタプログラミング―Excelを使ったDSLを作ろう―その1 メタプログラミングとは?
- 第4回 コードの分割―その6 Step4:クラスに分割する