Going my way

いいなと思ったことをメモしていきます。

さっそくテンプレートメソッドパターン使ってみたよ!


Advertisements


テンプレートメソッド(TempleteMethod)とは、読んで字のごとく、
テンプレート(型)を作って、テンプレに沿って実装するパターンである。
f:id:go_my_own_way:20050923143836j:plain

interfaceで型を決めて、実装はサブクラスに任せる。
クラスを使う側は、型だけ知っていれば良い。

せっかくテンプレートメソッドパターンを学んだから、実装してみた。

要件は以下の通り。

hoge	fuga	foo
hoge2	fuga2	foo2

タブ区切りのファイルを読み込んで、データの粒を取り出して標準出力に出力する。

同じようにカンマ区切りのファイルを読み込み、データを取り出して標準出力する。

hoge,fuga,foo
hoge2,fuga2,foo

これらには共通の処理があるので、それをテンプレートにする。
共通の処理はこのようなものがある。

・ファイルを開く(open)
・ファイルを読む(read)
・ファイルのデータを出力する(print)
・ファイルを閉じる(close)

クラス図にすると、こんな感じ。
f:id:go_my_own_way:20120811002823p:plain

では、ソースコードを見てみよう。

package templeteMethod;

import java.io.BufferedReader;

public interface ReadFile {
	public BufferedReader open(String fileName);
	public void read(BufferedReader inputFile);
	public void print(String line);
	public void close(BufferedReader inputFile);
	
}

これはテンプレートとなるインターフェースだ。
この型を使って、CSVファイルの読み込みとタブの読み込みを実装する。

ReadTabFile.java

package templeteMethod;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class ReadTabFile implements ReadFile{
	private static String fileName;
	File inputFile = null;
	BufferedReader in = null;
	
	public void setFileName(String fileName){
		fileName = this.fileName;
	}
	
	
	public BufferedReader open(String fileName){
		try{
			inputFile = new File(fileName);
			in = new BufferedReader(new FileReader(inputFile));
			return in;
		} catch(FileNotFoundException e){
			e.printStackTrace();
		}
		return null;
	}
	
	public void read(BufferedReader inputFile){
		try {
			String line;
			while((line = inputFile.readLine()) != null){
				print(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void print(String line){
		Pattern p = Pattern.compile("\t");
		String pieces[] = p.split(line);
		List<String> csvList = new ArrayList<String> ();
		
		for(int i=0; i<pieces.length;i++){
			csvList.add(pieces[i]);
		}
		
		for(String word : csvList){
			System.out.println(word);
		}
		
	}
	
	public void close(BufferedReader inputFile){
		try{
			inputFile.close();
		}catch(IOException e){
			e.printStackTrace();
		}
	}

}

タブの区切りは1行を正規表現「\t」で区切って配列にして格納した。

次はCSVファイルの処理。

package templeteMethod;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class ReadCsvFile implements ReadFile {
	private static String fileName;
	File inputFile = null;
	BufferedReader in = null;
	
	public void setFileName(String fileName){
		fileName = this.fileName;
	}
	
	
	public BufferedReader open(String fileName){
		try{
			inputFile = new File(fileName);
			in = new BufferedReader(new FileReader(inputFile));
			return in;
		} catch(FileNotFoundException e){
			e.printStackTrace();
		}
		return null;
	}
	
	public void read(BufferedReader inputFile){
		try {
			String line;
			while((line = inputFile.readLine()) != null){
				print(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void print(String line){
		Pattern p = Pattern.compile(",");
		String pieces[] = p.split(line);
		List<String> csvList = new ArrayList<String> ();
		
		for(int i=0; i<pieces.length;i++){
			csvList.add(pieces[i]);
		}
		
		for(String word : csvList){
			System.out.println(word);
		}
		
	}
	
	public void close(BufferedReader inputFile){
		try{
			inputFile.close();
		}catch(IOException e){
			e.printStackTrace();
		}
	}


}

CSVファイルもPatternクラスを使って「,」で区切った。
見ればわかるけど、タブ区切りとCSVファイルを読み込むクラスは正規表現の部分しか違わない。
あれ?テンプレートメソッドパターンなんて使わないで、1つのクラスに正規表現を渡せば済んだのでは?
というツッコミはスルーしよう。
なぜならこれはテンプレートメソッドの練習だから。

でお待ちかね、Mainメソッドはこちら。

package templeteMethod;

import java.io.BufferedReader;

public class Main {
	private static String CSVFile = "C:\\temp\\CSV.txt";
	private static String TabFile = "C:\\temp\\Tab.txt";
	private static BufferedReader in = null;
	
	public static void main(String[] args){
		
		//オブジェクトの参照を代入する型がReadFileであることに注目!!
		ReadFile readCsv = new ReadCsvFile();
		ReadFile readTab = new ReadTabFile();
		
		
		System.out.println("----------------------");
		System.out.println("カンマ区切りのデータを取り出す");
		//CSVファイルを読み込んで出力する。
		//Mainクラスを使う人は、ReadFileのインターフェースだけ知ってればいい。
		in = readCsv.open(CSVFile);	
		readCsv.read(in);
		readCsv.close(in);
		
		System.out.println("----------------------");
		System.out.println("タブ区切りのデータを取り出す");
		//Tab区切りファイルを読み込んで出力する
		in = readTab.open(TabFile);
		readTab.read(in);
		readTab.close(in);
		
		
	}
}

注目すべきはここ。

		//オブジェクトの参照を代入する型がReadFileであることに注目!!
		ReadFile readCsv = new ReadCsvFile();
		ReadFile readTab = new ReadTabFile();

型がReadFileなんですよね。
つまり、クラスを使うMain側から見ると、インターフェースのメソッドだけ知ってればいいってこと。
この観点はけっこう重要で、

・インターフェースは変わりづらい
・実装クラスは変わりやすい

ので、インターフェース型でメソッドを使うのは、変更に強いコードを書くために大切なことなのである。

実行した結果は以下のとおり

----------------------
カンマ区切りのデータを取り出す
hoge
fuga
foo
hoge2
fuga2
foo
----------------------
タブ区切りのデータを取り出す
hoge
fuga
foo
hoge2
fuga2
foo2

ちゃんとデータ取れてますね!