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

最終回 配列/コレクションを利用した抽象化―その1 配列/コレクションって何?

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

画像

本連載が書籍化されました。

良いコードを書く技術 ─ 読みやすく保守しやすいプログラミング作法

縣俊貴 著/A5判・240ページ
価格2394円(本体2280円)
ISBN 978-4-7741-4596-9

はじめに

「良いコード」をテーマにお送りしてきた本連載も,いよいよ最終回です。今回のテーマは「配列/コレクションを利用した抽象化」です。内容としては初歩的で当たり前過ぎるものですが,初心者の人はあまり意識的でなかったり,理解が足りていないことが多い部分でもあります。今回は連載に彩りを与えてくれたあの3人に,ペアプログラミングならぬトリオプログラミングで楽しく進行してもらいます。それではさっそく始めましょう。

配列/コレクションって何?

配列とコレクションはどちらもオブジェクトの集合(リスト)を格納・管理できる入れ物(コンテナとも呼ばれます)です。どちらもfor文などを使い,要素を繰り返し操作していくことができます。

配列とコレクションは似ていますが,Javaでは別物です注1)。配列はコレクションと比べると「大きさを簡単に変更できない」「集合を操作するAPIが貧弱」など制限が大きいのですが,コレクションより高速という特徴があります。配列を使った代表的なコードは次のようになります。

// 配列の大きさは3。あとから変更はできない!
String[] names = new String[] {"taro", "hana", "masaru"};
for (String name : names) {
    System.out.println(name);
}

コレクションとは通常,Java 1.2から追加されたコレクションAPIのことを意味します注2)。配列と比べると統一的な操作用のAPIがそろっていてたいへん扱いやすいです。コレクションAPIの代表的なインタフェースとしては,Map,List,Setなどがあります。コレクションを使った代表的なコードは次のようになります。

// コレクション(List)のサイズはあとから拡張可能!
List<String> names = new ArrayList();
names.add("taro");
names.add("hana");
names.add("masaru");
for (String name : names) {
    System.out.println(name);
}

使い分けとしては,集合の大きさが最初から決まっていてあとから変わらないのであれば配列を使うのが良いでしょう。それ以外では,目的に応じてコレクションAPIのいずれかのインタフェースを実装したクラスを利用します。

注1)
RubyやJavaScriptなどの配列はコレクションとしての特性を持っていて,特に区別されません。
注2)
JDK 1.2のコレクションAPIを理解する(オブジェクト倶楽部)も参照してください。

配列/コレクションを利用した抽象化とは?

似たような処理を何度も実行する必要があるときに,ベタに書くと次のような疑似コードになることが多いのではないでしょうか。

処理1
  処理1-A
  処理1-B
処理2
  処理2-A
  処理2-B
処理3
  処理3-A
  処理3-B
    :
    :

似た処理を100件実行したい場合は,このようなコードが100回分続きます。ふぅ,考えただけで大変そうですね。コード量もかなりのものになりますし,メンテナンス性も悪く,バグを生みやすくなります。

ベタに書いた場合,処理1と処理2は「必要なデータの違い」「部分的な処理の違い」など些細な部分が異なるだけで,基本的な処理の構造や内容はほとんど同じになることが多いでしょう。

それらの違いの部分だけをうまいこと「配列/コレクション」として抜き出しておき,共通部分の処理をループで回しながら実行することで,簡潔に処理を書くことができるようになります。ループ構造に書き換えた場合,次のような疑似コードができあがります。

処理対象リスト = [データ1, データ2, データ3, ...]
ループ(処理対象リスト) {
  処理A
  処理B
}

これは,「違いの部分を配列/コレクションとして抽出した」と言うこともできますし,「共通処理を抽象化して括り出した」と言うこともできます。視点が異なるだけでどちらも同じことを指しています。配列/コレクション化してループで一気に処理を実行することで,次のメリットが生まれます。

重複する処理が減りバグが生まれにくい

重複するコードは共通部分としてまとめられるので,些細なコピー&ペーストミスなどが起きにくい

全体に影響する機能追加や修正を簡単に行える

共通の処理はループ内にまとめてあるので,全体に影響する機能追加はここで一括して行える。個別に違う部分への対応は,ループ内でif文などを使用してアドホック(その場限り)に対応するか,それぞれの処理オブジェクトが処理の違いを保持して実現するなどの方法で対応する注3

処理対象が増えてもコードはほとんど増えない

10件でも1,000件でもコード量はほとんど変わらない

デメリットとしては,想定している抽象化のポイントを間違えると痛い目にあうという点があります。こちらに関してはまたあとで詳しく考察します。

注3)
本誌Vol.48の本連載第5回メタプログラミング .. Excelを使ったDSLを作ろうでは,ストラテジパターンを使って処理の違いを実現しています。合わせて参照してください。

抽象化はお好き?

それでは,毎回好例になりました,代表者の人に「配列/コレクションを使った抽象化」についてのご意見をいただきましょう。

良い仕事をしたい普通のプログラマ

達人プログラマを目指す 初級~中級のプログラマ

達人プログラマ

連載最終回にしてはじめて中級プログラマが謙虚さを見せていますね。きっといろいろな失敗を通して成長したんでしょう,すばらしいことです。普通のプログラマが言っている「連番付き変数」に似た例はこのあとのお題でも取り上げています。

それでは,いよいよお題と実際のリファクタリングの過程を見ていきましょう。

著者プロフィール

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

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

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

Twitter:@agata

コメント

コメントの記入