メリットは「生成と処理を分離し利用側の変更なしに処理(クラス)を追加したりできる」ことである。
■コード
今回使用するのは以下のxmlとcsvファイルの2種類とする。
xml
<data> <person> <name>John</name> <occupation>racer</occupation> </person> <person> <name>Mike</name> <occupation>banker</occupation> </person> <person> <name>Nick</name> <occupation>runner</occupation> </person> </data>
csv
John,racer
Mike,banker
Nick,runner
template methodで処理の枠だけ定義する。
interface Printer { public function read(); public function output(); }
以下のCsvFilePrinterクラスとXmlFilePrinterクラスで具体的な処理を記述する。
class CsvFilePrinter implements Printer { private $_filename; private $_fh; public function __construct($filename) { $this->_filename = $filename; } public function read() { $this->_fh = fopen($this->_filename, 'r'); } public function output() { print("<table>"); while($data = fgetcsv($this->_fh)){ print("<tr>"); for($i = 0, $n = count($data); $i < $n; $i++){ print("<td>{$data[$i]}</td>"); } print("</tr>"); } print("</table>"); fclose($this->_fh); } } class XmlFilePrinter implements Printer { private $_filename; private $_rh; public function __construct($filename) { $this->_filename = $filename; } public function read() { $this->_rh = simplexml_load_file($this->_filename); } public function output() { print('<table>'); foreach($this->_rh->person as $person){ print('<tr>'); print("<td>{$person->name}</td>"); print("<td>{$person->occupation}</td>"); print('</tr>'); } print('</table>'); } }
Factory部分
分岐してインスタンスを返すようになっている。
class PrinterFactory { public function getPrinter($filename) { if(preg_match('/\.csv$/i', $filename)){ return new CsvFilePrinter($filename); } elseif(preg_match('/\.xml$/i', $filename)){ return new XmlFilePrinter($filename); } } }
クライアントコード
以下のように中身を意識することなく使用できる。
$printer = new PrinterFactory(); $data = $printer->getPrinter('./dat.xml'); $data->read(); $data->output(); /* John racer Mike banker Nick runner */ $data = $printer->getPrinter('./dat.csv'); $data->read(); $data->output(); /* John racer Mike banker Nick runner */