からくりがてんこ

IT関連情報、プログラミングに関する作業ログや備忘録を記載していきます。

C言語からの本当のJava言語へ (TemplateMethod)

これからGofデザインパターンを紹介していきます。javaをどう使うかの部分を少しずつ掴んでいきましょう。

まずはTemplateMethodです。これは私が最初に出会ったデザインパターンです。
個人的には理解しやすく、メリットや設計イメージが付きやすい印象を持ちました。

TemplateMethod

端的に言うと、共通処理を親クラスにまとめ、独自の処理のみ継承した子クラスに実装するというものです。

どういった利点があり、具体的にどういった作りになるのか。簡単な例を挙げて見てみましょう。


例えば、カンマ区切りのファイルとタブ区切りのファイルの内容を同じ様に出力するようなシステムを考えた場合です。


処理の流れはこんな感じになりそうですね。

  • ファイルを開く
  • 読み込む
  • 解析
  • 表示
  • ファイルを閉じる

何となく想像して頂ければ分かるかもしれませんが、「解析」部分以外は、どちらのファイルを処理するとしても同じ動きをします。この解析処理がポッカリ空いた処理の流れをテンプレートとして親クラスに定義します。

細かい処理は割愛して、何をやってるかをコメント入りで書いています。

<<親クラスとなるテンプレートクラス>>

public abstract class FileCtrl {
    private void open() {
        // ファイルを開く処理
    }
    private String read() {
        // ファイルを読込み処理
        return "ファイルから読込んだ文字列を返します";
    }
    // ※抽象メソッド
    protected abstract String analize(String readData);

    private void close() {
        // ファイルを閉じる処理
    }

    // 外部のクラスからはこのメソッドを呼び出します。
    public void display() {
        // ここに一連の処理がテンプレート(処理の枠組み)として用意されています。
        // 動きが違うのはサブクラスで実装したanalizeメソッドの部分だけです。

        // ファイルを開く
        this.open();

        // ファイルを読込む
        String readData = this.read();

        // 解析する(この処理の実体はサブクラスに記載されています)
        String resultData = this.analize(readData);

        // ファイルを閉じる
        this.close();

        // 表示する
        System.out.println(resultData);
    }
}


その際のテクニックとして親クラスは抽象クラスで定義するところです。
そして、解析メソッドは抽象メソッド(abstract)として定義します。継承したサブクラスは必ずこの解析メソッドを実装しなければなりません。

抽象メソッドの使い所ですね。
抽象メソッドにすることで、プログラマが解析部分の実装を忘れる事はありませんし、親クラスだけで使用されることもありません。

では、継承したクラス「カンマ区切りファイル操作クラス」と「タブ区切りファイル操作クラス」を見てみましょう


<<カンマ区切りファイル操作クラス>>

public class CsvFileCtrl extends FileCtrl {
    @Override
    protected String analize(String readData) {
        // readDataはカンマ区切り
        // 例えば「鈴木,30,東京都」
        // カンマ区切りデータを決まった形で返す
        // 細かい処理は割愛します。。
        return "名前:鈴木、年齢:30、出身地:東京都";
    }
}

<<タブ区切りファイル操作クラス>>

public class TsvFileCtrl extends FileCtrl {
    @Override
    protected String analize(String readData) {
        // readDataはタブ区切り
        // 例えば「鈴木¥t30¥t東京都」
        // タブ区切りデータを決まった形で返す
        // 細かい処理は割愛します。。
        return "名前:鈴木、年齢:30、出身地:東京都";
    }
}


ずいぶんあっさりとしてますね。これだけでいいんです。
それぞれのクラスの役割がはっきりしていて、可読性もかなりいいですね。


実際それらのクラスを使うメインクラスの処理も見てみましょう。

<<メインクラス>>

public class TestMain {

    public static void main(String[] args) {
        FileCtrl fileCtrl = null;
        
        if (/*引数のファイル拡張子がcsvの時の判定*/) {
            // CSVファイルなのでCSV用のファイル操作クラスを生成
            fileCtrl = new CsvFileCtrl();			
        } else if (/*引数のファイル拡張子がtsvの時の判定*/) {
            // TSVファイルなのでTSV用のファイル操作クラスを生成
            fileCtrl = new TsvFileCtrl();
        }
        // 表示メソッドを呼び出す
        fileCtrl.display();
    }
}


メインクラスもなんと言うか見やすいですよね。


それをクラス図に表すとこうなります。
<<クラス図>>
f:id:ayumu-homes:20140118180031p:plain

クラス図のメソッドの前の記号は、アクセス修飾子を表しています。
「+」:public
「#」:protected
「-」:private

一度作ったプログラムというのは、「作ったので、はいそれで終わり」なんてことはほぼありません。
仕様の変更や追加が発生しますよね。
そういう時こそ、オブジェクト指向プログラミングが威力を発揮します。


例えば、仕様の追加として、スペース区切りのファイルやXMLファイルが仕様として追加されたという場合、どうすればよいでしょうか。


クラス図を見ながら考えてみましょう。



そうです、クラス図の「FileCtrl」クラスを継承したクラスを定義して、analizeメソッドの部分を追加するだけでいいんです。
(もちろんメインクラスにも修正入りますけど)


<<クラス図>>
f:id:ayumu-homes:20140118181826p:plain

どうですか、拡張しやすくて分かりやすくないですか?
追加したクラスのanalizeメソッドの動作確認をするだけなので、単体テストも大変楽です。


まだまだたくさん便利なパターンが存在します。
以前、「デザインパターンなんて使えない」という話を聞いた経験があるのですが、私はそうは思いません。
世の中「守・破・離」という言葉があるように、最初はマネから入っていきます。

ゼロからオブジェクト指向を学ぶ者が最終的に自分らしい設計ができるように考えを広げてくれる教科書的存在だと思っています。

まずはこのTemplateMethodを使ってみて、第一歩を踏み出してみて下さい。