其实没有那么高深,几句话就能说清楚。
问lz几个问题
(1)如果我写一个代码交给lz维护,你是愿意在我的代码中改来改去呢,还是直接再按照一定规则编写一段代码来扩展呢?
(2)如果lz是一个框架库的编写者(这听上去有点高级),你的用户不再是最终用户,而是一些初级程序员,你如何让他们觉得你的框架库好用。比如你的框架库应该适应尽可能多的情况(这些情况你不能一一预料到并且写在代码中),同时还要避免他们修改你的代码(你的代码已经编译成dll了)。
也许具体怎么做你不知道,但是看了我的两点,原则你已经知道了,那就是你编写的代码必须 对功能的扩展开放 (允许他们扩展功能),同时 对修改关闭 (程序本身不应该被修改,就像你不会随便修改System.Data,System.Web中的代码,实际上也没有代码给你修改)。
这个原则很多程序员已经意识到了,于是简称为OCP(开放关闭原则)。
那么接口、抽象类的作用是什么呢?它就是使得你的代码符合这个原则:
我们看这么一个场景,我们编写的程序提供了日志输出的功能:
这个程序作为直接给最终用户编码的程序,显然没有什么问题。
但是你可能意识到了,这个程序只能在控制台上才能显示错误,如果是在ASP.NET中,我们需要把输出的方式修改下才行。那么WinForms呢?我们还得修改。这对于一个通用的函数库来说,显得不可以接受。
当然你可以做一个判断,并且把这三种形式都包括在内。可是真的只有三种么?
如果调用你的程序员说,我想把错误输出到日志文件中,你不得不再增加第四种输出方式。
如果用接口,就没有问题了。
我们定义一个接口:
这个接口表示,我们允许用户传递一个可以包含WriteLine方法的类,具体什么类不重要,只要它能有这个方法就可以了。
我们的代码修改如下:
这样,我们的程序就把输出到哪里从程序中提取出来了,让调用者决定。
如果调用者在编写一个控制台程序,他可以这么用:
如果调用者在写asp.net程序,它只需要定义另一个类就可以了
很明显没,如果他想输出到文件,或者显示在别的地方,都只需要自己定义一个类,实现这个接口,再传进去就能办到了。也就是不修改你的代码,在你的代码上扩充,实现无限的扩展。
这就很好回答了最初的两个问题。
问lz几个问题
(1)如果我写一个代码交给lz维护,你是愿意在我的代码中改来改去呢,还是直接再按照一定规则编写一段代码来扩展呢?
(2)如果lz是一个框架库的编写者(这听上去有点高级),你的用户不再是最终用户,而是一些初级程序员,你如何让他们觉得你的框架库好用。比如你的框架库应该适应尽可能多的情况(这些情况你不能一一预料到并且写在代码中),同时还要避免他们修改你的代码(你的代码已经编译成dll了)。
也许具体怎么做你不知道,但是看了我的两点,原则你已经知道了,那就是你编写的代码必须 对功能的扩展开放 (允许他们扩展功能),同时 对修改关闭 (程序本身不应该被修改,就像你不会随便修改System.Data,System.Web中的代码,实际上也没有代码给你修改)。
这个原则很多程序员已经意识到了,于是简称为OCP(开放关闭原则)。
那么接口、抽象类的作用是什么呢?它就是使得你的代码符合这个原则:
我们看这么一个场景,我们编写的程序提供了日志输出的功能:
-
C# code
-
foo() { ... try { ... } catch (Exception ex) { Console.WriteLine(ex.Message); } ... }
这个程序作为直接给最终用户编码的程序,显然没有什么问题。
但是你可能意识到了,这个程序只能在控制台上才能显示错误,如果是在ASP.NET中,我们需要把输出的方式修改下才行。那么WinForms呢?我们还得修改。这对于一个通用的函数库来说,显得不可以接受。
当然你可以做一个判断,并且把这三种形式都包括在内。可是真的只有三种么?
如果调用你的程序员说,我想把错误输出到日志文件中,你不得不再增加第四种输出方式。
如果用接口,就没有问题了。
我们定义一个接口:
-
C# code
-
ILogger { void WriteLine( string content); }
这个接口表示,我们允许用户传递一个可以包含WriteLine方法的类,具体什么类不重要,只要它能有这个方法就可以了。
我们的代码修改如下:
-
C# code
-
foo(ILogger log) { ... try { ... } catch (Exception ex) { log.WriteLine(ex.Message); } ... }
这样,我们的程序就把输出到哪里从程序中提取出来了,让调用者决定。
如果调用者在编写一个控制台程序,他可以这么用:
-
C# code
-
class ConsoleLog : ILogger { public void WriteLine( string content) { Console.WriteLine(content); } } ... foo( new ConsoleLog());
如果调用者在写asp.net程序,它只需要定义另一个类就可以了
-
C# code
-
class ResponseLog : ILogger { public void WriteLine( string content) { Response.WriteLine(content); } } ... foo( new ResponseLog());
很明显没,如果他想输出到文件,或者显示在别的地方,都只需要自己定义一个类,实现这个接口,再传进去就能办到了。也就是不修改你的代码,在你的代码上扩充,实现无限的扩展。
这就很好回答了最初的两个问题。