您通常会遇到一个班级使用其他班级的服务的情况。 “使用服务”是指调用其公共方法。例如,CalculatorUser
使用MyCalc
- CalculatorUser
实例将在某处呼叫MyCalc.Sum()
。
想象一下,现在您将包含类CalculatorUser
和MyCalc
的应用程序交给您的客户。你可以写这样的事情:
public class CalculatorUser
{
private MyCalc _myCalc;
public CalculatorUser(MyCalc myCalc)
{
_myCalc = myCalc;
}
public void PerformAddition(int a, int b)
{
_myCalc.Sum(a, b);
}
}
class Program
{
static void Main(string[] args)
{
MyCalc calculator = MyCalc();
CalculatorUser calcUser = new CalculatorUser(calculator);
calcUser.PerformAddition(1, 2);
Console.ReadKey();
}
}
一切看起来不错,但随后一段时间后,客户回来给你带了新的要求:“我要CalculatorUser
有更多的选择,我希望它能够选择在旧的简单计算器MyCalc
和一个新的花式计算器之间显示操作数,操作和结果!而且,这个选择必须在运行时间中进行。“
您意识到现在您必须创建MyFancyCalc
并且还要更改CalculatorUser
以支持此新要求。您可能需要将MyFancyCalc
类型的另一个成员添加到CalculatorUser
,然后使用另一种方法PerformAdditionWithFancyCalc()
,这将使用MyFancyCalc
。但是如果你的客户需要10种其他类型的计算器 - 你会为每个计算器添加新的成员和方法吗?如果让用户和服务提供者紧密耦合,每次需求变化都会导致用户和解决方案的不断变化,这是因为用户不知道特定的服务提供者,而只知道它提供的服务:这些服务的名称是什么,它们的输入值的类型是什么,它们的输出类型是什么?这实际上是使服务提供商的公众界面。 CalculatorUser
不需要知道计算器的具体实现 - MyCalc
或MyFancyCalc
,它只需要知道它使用的任何计算器都有一个方法Sum
,它接受两个int
值并返回void
。通过这种方式,您可以将用户从特定的计算器中解耦出来,并使其能够使用任何以接口中描述的方式实现Sum
的计算器。如果您创建MyExtraFancyCalc
班,则不需要更改CalculatorUser
。
所以,以满足新的需求(使计算器选择在运行时),你可以写这样的事情:
public interface ICalculator
{
void Sum(int a, int b);
}
public class MyCalc : ICalculator
{
public void Sum(int a, int b)
{
Console.WriteLine(a + b);
}
}
public class MyFancyCalc : ICalculator
{
public void Sum(int a, int b)
{
Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
}
}
public class CalculatorUser
{
private ICalculator _calculator;
public CalculatorUser(ICalculator calculator)
{
_calculator = calculator;
}
public void PerformAddition(int a, int b)
{
_calculator.Sum(a, b);
}
}
class Program
{
static void Main(string[] args)
{
bool useFancyCalculator = GetUseFancyCalculator();
ICalculator calculator = CreateCalculator(useFancyCalculator);
CalculatorUser calcUser = new CalculatorUser(calculator);
calcUser.PerformAddition(1, 2);
Console.ReadKey();
}
static bool GetUseFancyCalculator()
{
Console.WriteLine("Would you like to use fancy calculator? (y/n)");
string choice = Console.ReadLine();
return (choice == "y");
}
static ICalculator CreateCalculator(bool createFancyCalculator)
{
ICalculator calculator = null;
if (createFancyCalculator)
calculator = new MyFancyCalc();
else
calculator = new MyCalc();
return calculator;
}
}
询问用户“你想使用花哨的计算器(Y/N) “,如果使用类型”n“,则使用旧计算器并且输出简单地为”3“,但如果答案为”y“,则使用花式计算器并且输出为”1 + 2 = 3“
此示例显示接口的强大功能(基本上就是一种称为依赖注入的简单模式)。
在现实生活中,您经常会遇到这样的情况:您的客户拥有一个应用程序(CalculatorUser
),该应用程序永远不会或很少发生更改,包含各种服务提供程序实现的插件(DLL)。主应用程序会在运行时检测哪些插件可用并选择一个,具体取决于运行时中生成的用户选择(或其他条件)。
在运行时**会有所不同:第一个被编译为il“callvirt”操作,而第二个只是一个“调用”操作(速度稍快)。 – Olivier