質問: $printFactory->createRandomHage(); $alwaysFuga->doPrint(); //fugaと表示 $randomHage->doPrint(); //hageと表示されたりされなかったり(※) ``` こちらのソースにはバグが有り、hageではなくhogeと表示されています(※)。 そしてこのバグをデバッグするときに、私はおおむね以下の様な順でバグ探しに行きます。 1:doPrint()で問題(※)が起きていて、それはcreateRandomHage()で作られて、さらにそれはPrintFactoryで作られた、とさかのぼる。 2:そこでPrintFactoryのcreateRandomHage()を見に行く。 3:そこで生成されている、PrintWithDecisionを見に行く。 4:**PrintWithDecisionを見て、doPrint()の中身はなんだ?と行き詰まる。** (この規模のソースでそれぐらいは覚えとけというツッコミや、単体テストはどうしたというツッコミもあるかと思いますが) この行き詰まる原因がどうも、オブジェクト指向の本質(?)にあるように思われました。 オブジェクト指向では、基本的にclassはinterfaceに依存するように作られているため、 2→3のように実際のclassを見に行くと、そこはinterfaceでしか書かれおらず、 次に見るべきソース(doPrintの中身)が不明で、行き詰まっていました。 そこで、タイトルの疑問なのですが、 **オブジェクト指向のソースコードは、本質的に処理を追いにくくなるものなのでしょうか。 もし、追いにくいものであるとしましたら、何かソースを追うコツとかあるのでしょうか。** よろしくお願いします。
回答: なかなか刺激的なタイトルなので、たくさん反応がありそうですね。 手続き型で書いてもオブジェクト指向で書いても、デバッグの難しさは変わらないです。(細かい点をのぞく。) 例として出されたコードはちょっと変なコードです。必要もないのにインターフェイスを多用しています。 インターフェイスは、確かに実際に実行されているクラスを判別するのには手間がかかります。ただし、実装がバラバラなオブジェクトを一つにまとめるにはよい方法です。 例では、doPrintメソッドを呼び出す前に、randを使うという点のみが相違点なので、継承を使うとわかりやすいと思います。というわけで、私が書くならというコードです。(とその前に、そもそもClassを定義しなくてもいいレベルのコードかもしれません。) ``` str = $str; } public function print(){ echo $this->str ; echo "\n"; } } //Printerオブジェクトのちょっとだけ変わったオブジェクトなので継承を使う class RamdamizePrinter extends Printer{ private $parsent; function __construct($str, $percent){ //そのまま使えるコードは再利用 parent::__construct($str); $this->percent = $percent; } public function print(){ if(rand(1, 100) <= $this->percent) { parent::print(); } } } class PrinterFactory { // どのオブジェクトを呼び出すべきかの判断は // 呼び出し元は気にしなくていいようにするのがFactoryの利点 public function create($value){ if($value == 'hoge') { return new RamdamizePrinter('hoge', 50); } else { return new printer($value); } } } $arr = array("fuga", "hoge"); $printerFactory = new PrinterFactory(); //同じ変数に入れるなどしないと、多態の意味がない foreach ($arr as $value) { $some = $printerFactory->create($value); $some->print(); } ?> ``` これなら、比較的わかりやすいと思います。 厳しい言い方になりますが、i50さんが指摘している部分は不要なメソッドが多いコードを書いたというだけで、この書き方では手続き型で書いても同じくらい追いづらいです。 また、新しい環境のようにオブジェクト指向の書き方に慣れていないから追いづらいということも大いにあると思います。 インターフェイスのみが多態を行う方法ではないですし、多態のみではオブジェクト指向について判断するには早すぎると思います。 オブジェクト指向プログラミングは、コードのコピペを減らしたり、メソッドの変数を減らしたり、複数のメソッドをまとめたりすることで、コードをわかりやすくまとめることができます。 特にオブジェクト指向でなければ書けないプログラムはないので、適材適所で利用すればいいと思いますし、便利なところで利用するのが習得の早道だと思います。