接口
问题的提出
定义:开发一个应用,使不同型号的打印机都能与电脑连接
上下文(环境):为了满足需求,公司现在购置了两类打印机,即打印机A和打印机B,它将与公司1000名员工的电脑连接,实现通信。并且公司在以后可能还会购买第三类打印机。
分析上述问题,可能有如下方案
方案一:分别定义PrintA和PrintB两个类。实现各自的Receive和Print方法,这么一来总共需要写四个方法。这种方法应该是最直观,最容易让人想到的。
方案二:定义抽象类Printer,在里面写虚方法 Receive和 Print,使得这两台打印机分别继承此抽象类,并重写 Receive和Print方法。Computer类中包含一个类型为 Printer的成员变量,并为其编写get/set器,这样 Computer中只需要两个方法: ReceiveData和 PrintData,并通过多态性实现不同移动设备的读写。这种方法应该是接着会被想到的方法。
方案三:与方案二基本相同,只是不定义抽象类,而是定义接口I Printer,修改打印机类。Computer中通过依赖接口IPrinter实现多态性。
现在我们来分析一下上面三种方法。首先第一种方法最为直白,实现起来最简单,但是它有一个致命的弱点:可扩展性差。当将来有了第三中类型的打印机时,必须对Computer进行修改。这就如在一个真实的计算机上,为每一种打印机设备实现一个不同的插口、并分别有各自的驱动程序。当有了一种新的打印机设备后,我们就要将计算机大卸八块,然后增加一个新的插口,在编写一套针对此新设备的驱动程序。这种设计显 然不可取。
此方案的另一个缺点在于,冗余代码多。在题目所给的环境中,已经说明打印机将与1000台电脑连接,这么一来,总共就需要写四千中方法。代码的重复量是相当大的。我们再来看方案二和方案三,之所以将这两个方案放在一起讨论,是因为他们基本是一个方案(从思想层面上来说),只不过实现手段不同,一个是使用了抽象类,一个是使用了接口,而且最终达到的目的应该是一样的。
我们先来评价这种方案:首先它解决了代码冗余的问题,因为可以动态替换打印机,并且都实现了共同的接口,所以不管有多少种移不同类型的打印机,只要一个 Receive方法和一个Print方法,多态性就帮我们解决问题了。而对第一个问题,由于可以运行时动态替换,而不必将打印类编码在Computer 中,所以有了新的第三方设备,完全可以替换进去运行。这就是所谓的“依赖接口,而不是依赖于具体类”。如此一来,Computer和打印机类的耦合度大大下降。
我们先来看一段断码:
1.定义一个接口
namespace _1.InterfaceTest { /// <summary> /// 定义打印机接口,不同类型的打印机都有共同的属性 /// 他们都会接受电脑的信息(Receive)并且执行打印命令(Print)。 /// </summary> public interface IPrinter { void Receive(); void Print(); } } |
namespace _1.InterfaceTest { /// <summary>
/// 实现A类打印机
/// </summary>
public class PrinterA : IPrinter
{
/// <summary>
/// 实现接口定义的Receive方法
/// </summary>
public void Receive()
{
Console.WriteLine("PrinterA receive message from Computer……");
Console.WriteLine("Receive finished!");
}
/// <summary>
/// 实现接口定义的Print方法
/// </summary>
public void Print()
{
Console.WriteLine("PrinterA execute the Computer command ……");
Console.WriteLine("Print finished!");
}
}
/// <summary>
/// 实现B类打印机
/// </summary>
public class PrinterB : IPrinter
{
/// <summary>
/// 实现接口定义的Receive方法
/// </summary>
public void Receive()
{
Console.WriteLine("PrinterB receive message from Computer……");
Console.WriteLine("Receive finished!");
}
/// <summary>
/// 实现接口定义的Print方法
/// </summary>
public void Print()
{
Console.WriteLine("PrinterB execute the Computer command ……");
Console.WriteLine("Print finished!");
}
}
}
|
namespace _1.InterfaceTest { /// <summary>
/// 电脑与打印机连接,不论是何种打印机,电脑都将调用这两个方法
/// </summary>
public class Computer
{
/// <summary>
/// 定义接口类型的变量
/// </summary>
private IPrinter _print;
public IPrinter Print
{
get
{
return this._print;
}
set
{
this._print = value;
}
}
public Computer()
{
}
public Computer(IPrinter print)
{
this.Print = print;
}
public void ReceiveData()
{
this._print.Receive();
}
public void PrintData()
{
this._print.Print();
}
}
}
}
|
4.测试一下打印机是否能正常工作:
namespace _1.InterfaceTest
{
class Program
{
static void Main(string[] args)
{
Computer computer = new Computer();
IPrinter printerA = new PrinterA();
IPrinter printerB = new PrinterB();
Console.WriteLine("I connected PrinterA into computer and print something");
computer.Print = printerA;
computer.ReceiveData();
computer.PrintData();
Console.WriteLine("-------------------------------------------------------------“);
Console.WriteLine("Now,PrinterA has some problem!I connected PrinterB into computer and print something");
computer.Print = printerB;
computer.ReceiveData();
computer.PrintData();
}
}
}
|
输出结果:
测试成功!
此时又有另外一个问题,公司又重新购置了一台不同类型的打印机,那又该如何实现呢?过程简单。代码如下:
namespace _1.InterfaceTest
{
class Program
{
static void Main(string[] args)
{
Computer computer = new Computer();
IPrinter newPrinter = new NewPrinter();
Console.WriteLine("I connected newPrinter into computer and print something");
computer.Print = newPrinter;
computer.ReceiveData();
computer.PrintData();
Console.ReadLine();
}
}
}
|
哈哈,是不是很神奇,Computer一点都不用改动,就可以使新的设备正常运行。这就是所谓“对扩展开放,对修改关闭”。