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

第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);
    ...
}
...
図3 Step3のクラス図
図3 Step3のクラス図

考察:制御と処理の分割のよくある例

制御と処理を分ける場合によくあるパターンを紹介します。

ループと、ループの中の処理

ループの中では、リストや配列の中の要素を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]⁠。例外キャッチ時の変数名も見所です。

おすすめ記事

記事・ニュース一覧