良いコ-ドへの道―普通のプログラマのためのステップアップガイド
第4回 コードの分割―その5 Step3:処理単位で分割する
Step3:処理単位で分割する
次に,処理全体の流れをわかりやすくするためのメソッド分割を行います。indexメソッドの処理は大きく3つに分かれています。
- 処理1:XML用の組織,ユーザデータを取得
- 処理2:XMLの作成
- 処理3:XMLの出力
このうち処理2,処理3は「Documentオブジェクトの構築」「DocumentオブジェクトをXML出力」という別メソッドに分けることができそうです。何を行う処理かわかりやすいようにメソッド名は「buildDocument」「writeDocument」にします。変更後のコードはリスト5,図3になりました。indexメソッドはたったの4行になりました。このように「処理の流れ(フロー)・制御」の部分と「具体的な処理」を分割することで,処理全体の流れが明確になります。また,メソッド名が処理の内容を表しているので説明的なコードになり,コメントなしでコードの意味がわかるようになりました。
リスト5 Step3:処理単位で分割する
...
public ActionResult index() throws Exception {
List<Division> divs = userService.getDivisions();
List<User> users = userService.getUsers();
Document doc = buildDocument(divs, users);
writeDocument(doc);
}
private Document buildDocument(List<Division> divs, List<User> users)
throws ParserConfigurationException {
int rowIndex = 1; ―①
Document doc = newDocument();
Element rootNode = doc.createElement("data");
doc.appendChild(rootNode);
Element divisionsNode = doc.createElement("divisions");
for (Division div: divs) {
Element node = doc.createElement("division");
node.setAttribute("index", String.valueOf(rowIndex++)); ―②
...
}
rootNode.appendChild(divisionsNode);
Element usersNode = doc.createElement("users");
for (User user: users) {
Element node = doc.createElement("division");
node.setAttribute("index", String.valueOf(rowIndex++)); ―③
...
}
rootNode.appendChild(usersNode);
...
}
private void writeDocument(Document doc) {
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
DOMSource source = new DOMSource(doc);
...
}
...
- 注11)
- staticな共通処理のメソッドのことです。詳細は本誌Vol.22の特集1「サルでもわかる逆引きデザインパターン」(本誌総集編に収録されています)か,そのWeb掲載版であるhttp://www.nulab.co.jp/designPatterns/designPatterns4/designPatterns4-1.htmlをご覧ください。
考察:制御と処理の分割のよくある例
制御と処理を分ける場合によくあるパターンを紹介します。
ループと,ループの中の処理
ループの中では,リストや配列の中の要素を1件ずつ処理することが多いと思います。この場合,「ループの制御構造」と「要素の1件を処理するメソッド」に分割できます。「要素の1件を処理するメソッド」はループ以外からも利用できるので,再利用性も高まります。
for (Item item : items) {
if (item.isNotEmpty()) {
...
...(長いコード)
...
}
}
![]()
for (Item item : items) {
process(item)
}
private void process(Item item) {
if (!item.isNotEmpty()) { return; }
...
...(長いコード)
...
}
// ループ以外からも利用可能!
process(getLastItem());
if文とその中の処理
if文の中の長い処理を別メソッドに分割することで,説明的かつ明確になります。以下のコードにはあえてコメントを付けていますが,なくても十分わかりやすいコードではないでしょうか。
if (cart.isEmpty()) {
// カートが空ならエラー
loggingError(cart);
} else {
// それ以外なら精算
checkout(cart);
}
try~catch~finallyとその中の処理
例外処理では「制御構造」と「具体的な処理」を分割することで,処理の流れが明確なコードを書くことができます。
次のコードはJUnit 4.4のTestCase#runBareメソッドの抜粋です。
setUp();
try {
runTest();
} catch (Throwable running) {
exception= running;
} finally {
try {
tearDown();
} catch (Throwable tearingDown) {
if (exception == null) exception= tearingDown;
}
}
制御構造のtry~catch~finally と,処理のsetUp,runTest,tearDownが分かれていて,処理の流れがわかりやすくたいへん読みやすいコードになっています(注12)。例外キャッチ時の変数名も見所です。
- 注12)
- このコードが別メソッドに分割されているもともとの目的は,下位クラスでそれぞれのメソッドをオーバーライドできるようにするためです。ここでは制御構造と処理が分離されたコードの良い例として取り上げています。このように,制御構造と処理のメソッドを分けて変更したい処理だけを下位クラスでオーバーライドする設計パターンを「テンプレートメソッド」といいます。テンプレートメソッドについては,先述した本誌Vol.22の特集1か,そのWeb 掲載版であるhttp://www.nulab.co.jp/designPatterns/designPatterns3/designPatterns3-1.htmlを参考にしてください。
良いコ-ドへの道―普通のプログラマのためのステップアップガイド
- 最終回 配列/コレクションを利用した抽象化―その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:クラスに分割する


