すべての回答が理論に重点を置いているので、私は最初のアプローチの例で実証したいと思います:
注文の出荷後にSMS確認メッセージを送信する機能を含むアプリケーションを構築しているとします。SMSの送信を担当するクラス(SMSService)とユーザー入力のキャプチャを担当するクラス(UIHandler)の2つのクラスがあります。コードは次のようになります。
public class SMSService
{
public void SendSMS(string mobileNumber, string body)
{
SendSMSUsingGateway(mobileNumber, body);
}
private void SendSMSUsingGateway(string mobileNumber, string body)
{
/*implementation for sending SMS using gateway*/
}
}
public class UIHandler
{
public void SendConfirmationMsg(string mobileNumber)
{
SMSService _SMSService = new SMSService();
_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
}
}
上記の実装は間違っていませんが、いくつかの問題があり
ます。-)開発環境で、SMSゲートウェイを使用する代わりにテキストファイルに送信されたSMSを保存して、これを実現するとします。(SMSService)の具体的な実装を別の実装に変更することになります。この場合、柔軟性が失われ、コードの書き換えが強制されます。
-)結局、クラスの責任が混在することになり、私たちの(UIHandler)は(SMSService)の具体的な実装について決して知らないはずです。これは、「インターフェース」を使用してクラスの外部で行う必要があります。これが実装されると、同じインターフェースを実装する別のモックサービスで使用される(SMSService)を交換することにより、システムの動作を変更できるようになります。このサービスは、SMSをmobileNumberに送信する代わりにテキストファイルに保存します。
上記の問題を修正するために、(SMSService)と新しい(MockSMSService)によって実装されるインターフェイスを使用します。基本的に、新しいインターフェイス(ISMSService)は、以下のコードと同じように両方のサービスの動作を公開します。
public interface ISMSService
{
void SendSMS(string phoneNumber, string body);
}
次に、(SMSService)実装を変更して(ISMSService)インターフェースを実装します。
public class SMSService : ISMSService
{
public void SendSMS(string mobileNumber, string body)
{
SendSMSUsingGateway(mobileNumber, body);
}
private void SendSMSUsingGateway(string mobileNumber, string body)
{
/*implementation for sending SMS using gateway*/
Console.WriteLine("Sending SMS using gateway to mobile:
{0}. SMS body: {1}", mobileNumber, body);
}
}
これで、同じインターフェースを使用して、まったく異なる実装で新しいモックアップサービス(MockSMSService)を作成できます。
public class MockSMSService :ISMSService
{
public void SendSMS(string phoneNumber, string body)
{
SaveSMSToFile(phoneNumber,body);
}
private void SaveSMSToFile(string mobileNumber, string body)
{
/*implementation for saving SMS to a file*/
Console.WriteLine("Mocking SMS using file to mobile:
{0}. SMS body: {1}", mobileNumber, body);
}
}
この時点で、次のように(UIHandler)のコードを変更して、サービス(MockSMSService)の具象実装を簡単に使用できます。
public class UIHandler
{
public void SendConfirmationMsg(string mobileNumber)
{
ISMSService _SMSService = new MockSMSService();
_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
}
}
私たちは多くの柔軟性を実現し、コードに懸念の分離を実装しましたが、それでも2つのSMSサービスを切り替えるためにコードベースに変更を加える必要があります。したがって、依存性注入を実装する必要があります。
これを実現するには、(UIHandler)クラスコンストラクターに変更を実装して依存関係を渡す必要があります。これにより、(UIHandler)を使用するコードは、使用する(ISMSService)の具体的な実装を決定できます。
public class UIHandler
{
private readonly ISMSService _SMSService;
public UIHandler(ISMSService SMSService)
{
_SMSService = SMSService;
}
public void SendConfirmationMsg(string mobileNumber)
{
_SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
}
}
これで、クラス(UIHandler)と対話するUIフォームは、使用するインターフェース(ISMSService)の実装を渡す責任があります。これは、コントロールを反転したことを意味します。(UIHandler)は、どの実装を使用するかを決定する責任がなくなり、呼び出し側のコードが行います。DIがその1つのタイプである制御の反転原理を実装しました。
UIフォームコードは次のようになります。
class Program
{
static void Main(string[] args)
{
ISMSService _SMSService = new MockSMSService(); // dependency
UIHandler _UIHandler = new UIHandler(_SMSService);
_UIHandler.SendConfirmationMsg("96279544480");
Console.ReadLine();
}
}