由于所有答案都强调理论,因此我想以第一种示例方式进行演示:
假设我们正在构建一个应用程序,其中包含一个功能,该功能可在订单发货后发送SMS确认消息。我们将有两个类,一个类负责发送SMS(SMSService),另一类负责捕获用户输入(UIHandler),我们的代码如下所示:
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!");
}
}
我们已经在代码中实现了很大的灵活性并实现了关注点分离,但是仍然需要对代码库进行更改,才能在两个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是其中的一种。
UI表单代码如下:
class Program
{
static void Main(string[] args)
{
ISMSService _SMSService = new MockSMSService(); // dependency
UIHandler _UIHandler = new UIHandler(_SMSService);
_UIHandler.SendConfirmationMsg("96279544480");
Console.ReadLine();
}
}