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

第4回 コードの分割―その5 Step3:処理単位で分割する

この記事を読むのに必要な時間:およそ 2 分

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);
    ...
}
...

図3 Step3のクラス図

図3 Step3のクラス図

注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を参考にしてください。

著者プロフィール

縣俊貴(あがたとしたか)

学生時代にMSXで制限された環境でのプログラミングの楽しさを学ぶ。以来,オープンソースのWiki実装「MobWiki」の開発や受託開発などを経て,現在はプロジェクト管理ツール「Backlog」,ドローツール「Cacoo」など,コラボレーション型のWebサービスの企画と製品開発を行う。また,Webアプリケーションフレームワーク「Cubby」のコミッタを務める。福岡在住。株式会社ヌーラボ所属。

ブログ :http://d.hatena.ne.jp/agt

Twitter:@agata

コメント

コメントの記入