ご無沙汰しております。今日は久しぶりにプログラマっぽいことを書いてみます。
SpringFrameworkやGuiceといったDIコンテナを触っていて、いつも思うことがあります。
それは、「DIのためのインタフェースって必要?」ってことです。
よく本には「インタフェースを定義しておくことで、テスト時にモックと入れ替えることができる」とありますが、
別にインタフェースを使わなくてもできるんじゃないの?って思ってます。
実クラスにすると、IDEでインタフェースにジャンプしてイラっとしたりしませんし(笑)
例えば、(Guiceのコードからアイデアを貰いましたが)FrogManというクラスがあるとします。
そのクラスはVehicle(乗り物)というフィールドを持ち、scramble(緊急発進)というメソッドを持つとします。
Vehicleクラスの中身が出来ていない段階で、FrogManクラスをテストしたいとします。
DIの本ではVehicleインタフェースとVehicleMockクラスを作ってテストし、
本番ではVehicleImplクラスで実行するって感じだと思います。
でも、とりあえず中身が空のVehicleクラスを作っておいて、
テスト用のVehicleMockクラスはVehicleクラスを継承したらダメなんでしょうか?
文字だけならイメージが湧かないかもしれませんので、Guiceを使ったコードを書いてみます。
まず、FrogManクラスを作ります。
注入されたVehicleクラスのscrambleメソッドを呼び出す、同名のメソッドを用意しておきました。
public class FrogMan {
@Inject
private Vehicle vehicle;
public void scramble() {
this.vehicle.scramble();
}
}
次に、Vehicleクラスを作ります。とりあえず製作中だとします。
public class Vehicle {
public void scramble() {
System.out.println("製作中・・・");
}
}
次のようにして実行してみます。
FrogManクラスやVehicleクラスは実クラスですので、bindしなくても大丈夫みたいです。
Injector injector = Guice.createInjector(
new AbstractModule() {
@Override
protected void configure() {
}
}
);
FrogMan instance = injector.getInstance(FrogMan.class);
instance.scramble();
実行すると、画面には次のように表示されます。
製作中・・・
ちゃんと注入されているようですね。次に、テスト用のVehicleMockクラスを作ります。
public class VehicleMock extends Vehicle {
@Override
public void scramble() {
System.out.println("モックです");
}
}
テストでは次のようにして実行します。
モックを使うので、VehicleクラスにVehicleMockクラスをbindしています。
Injector injector = Guice.createInjector(
new AbstractModule() {
@Override
protected void configure() {
bind(Vehicle.class).to(VehicleMock.class);
}
}
);
FrogMan instance = injector.getInstance(FrogMan.class);
instance.scramble();
テストを実行すると、画面には次のように表示されます。
モックです
ちゃんとモッククラスに入れ替わっているようですね。
この方法だと、テストではVehicleのscrambleメソッドの中身に関係なく実行することができるようになりますし、
インタフェースも作らなくてよくなります。
・・・これじゃダメなんですかね?