さっそくテンプレートメソッドパターン使ってみたよ!
Advertisements
テンプレートメソッド(TempleteMethod)とは、読んで字のごとく、
テンプレート(型)を作って、テンプレに沿って実装するパターンである。
interfaceで型を決めて、実装はサブクラスに任せる。
クラスを使う側は、型だけ知っていれば良い。
せっかくテンプレートメソッドパターンを学んだから、実装してみた。
要件は以下の通り。
hoge fuga foo hoge2 fuga2 foo2
タブ区切りのファイルを読み込んで、データの粒を取り出して標準出力に出力する。
同じようにカンマ区切りのファイルを読み込み、データを取り出して標準出力する。
hoge,fuga,foo hoge2,fuga2,foo
これらには共通の処理があるので、それをテンプレートにする。
共通の処理はこのようなものがある。
・ファイルを開く(open)
・ファイルを読む(read)
・ファイルのデータを出力する(print)
・ファイルを閉じる(close)
クラス図にすると、こんな感じ。
では、ソースコードを見てみよう。
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
ちゃんとデータ取れてますね!