インターフェイスまたは少なくとも抽象/継承可能なクラスを使用する場合、DI/IoC 構成で実装を簡単に交換 (別のクラスを挿入) することで、プログラムの動作を変更できます。
インターフェースを使用することは良い習慣です(imho)。これは、モックを必要とする UnitTests を作成している場合に特に重要です。インターフェースを使用していない場合、適切なカバレッジで UnitTests を作成することは非常に困難です (ほとんどの「現実世界の」ケースでは不可能とは言えません)。
注入部分が変化する可能性がある場合は、インターフェイスを使用する必要があると思います。実装を拡張するのは簡単なはずです。 Open-Closed-Principleを参照してください。=> これには、モジュール/パーツ/実装の交換が必要になります...クラスにオーバーライドする仮想関数がなく、実装を変更せざるを得ない場合にどうなるか自問してください。
少なくともパブリッククラス/コードの一部(他のプログラマーが使用する部分)にはインターフェイスを使用します。
あなたのサンプルを見てください。問題はワイヤリング部分にあり、インターフェースの (デフォルト) 実装としてのクラスのバインディングだけではありません (バインディングは機能しますが、ワイヤリングが壊れる可能性があります)。
たとえば、2 つの実装がある場合 (ここの C# サンプルは、Java などでも同じである必要があります):
public interface IUserStorage
{
void Write(object something);
}
public class UserStorageTextFile : IUserStorage
{
public void Write(object something) { ... }; // stores to text file
}
public class UserStorageDB : IUserStorage
{
public void Write(object something) { ... }; // stores to DB
}
public class MyStorageClient
{
public MyStorageClient(IUserStorage storage) { ... } // copy to private field and use it etc.
}
IoC によっては、MyStorageClient のインスタンスを IUserStorage のバインディングに簡単に接続できるはずです。
bind(IUserStorage.class).to(UserStorageDB.class); // Java sample, eh?
しかし、MyStorageClient がすでに DB を使用することを強く強制されている場合...
public class MyStorageClient
{
public MyStorageClient(UserStorageDB storage) { ... } // copy to private field and use it etc.
}
... UserStorageTextFile が UserStorageDB から継承されていることを除いて、UserStorageTextFile クラスと結び付けることは不可能です...しかし、単純なテキストファイルのみを書きたい場合、たとえば Oracle ドライバー (UserStorageDB によって必要とされる) に依存する必要があるのはなぜですか? ?
サンプルは十分に明確で、インターフェースを使用する利点を示していると思います...
そうでない場合は...これを試してください:
bind(UserStorageDB.class).to(UserStorageTextFile.class);
// and in another config/module/unitTest
bind(UserStorageTextFile.class).to(Mock(UserStorageDB.class));
// and try to wire it against your client class, too (both ways, meaning one config for TextFile and load a config for the DB after changing only the configuration)