三、里氏替换原则

里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing教授于1994年提出。其严格表述如下:如果对每个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换o2时,程序P的行为没有变化,那么类型S是类型T的子类型。这个定义比较拗口且难以理解,因此一般使用它的另一个通俗版定义:

里氏代换原则(Liskov Substitution Principle,LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。

里氏替换原则强调。程序设计中,一个地方可以使用父类对象,那么一定可以使用其子类替换。因为子类是父类的泛化,父类定义的行为和属性,在子类中一定有对应的实现。里氏替换原则是实现开闭原则的保证,只要满足该原则,我们可以在定义对象时使用父类,在程序运行时使用子类来替换,这样可以很方便的扩展系统的功能。

 

四、依赖倒转原则

依赖倒转原则是Robert C.Martin在1996年为“C++Reporter”所写的专栏Engineering Notebook的第3篇,后来加入他在2002年出版的经典著作Agile Software Development,Principles,Patterns,and Practices一书中。依赖倒转原则定义如下:

依赖倒转原则(Dependency Inversion Principle,DIP):抽象不应该依赖于细节,细节应该依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。

依赖倒转原则强调,在关联关系或者依赖关系中,程序应该引用层次更高的抽象类,而不是具体的类。抽象类被用来设计处理一些共性的问题。这样当系统的行为发生变动时,可以通过增加具体的类的实现来解决具体问题,降低影响范围。

 

里氏替换原则和依赖倒转原则常常被放在一起使用,用来增加程序的扩展性。下面来看一个例子:

有一个服务器应用,需要得到配置文件的数据,配置文件的类型可以是INI,XML,JSON,未来还有可能增加新的文件类型。在这种情况下,我们可以设计一个"文件解析者"的抽象类,专门用来解析文件的内容,并让他的子类作为具体类,对某一种具体文件的进行具体的解析。

 1 #include"iostream"
 2 
 3 class fileParser
 4 {
 5     public:
 6         virtual void parseContent() = 0;
 7 };
 8 
 9 class service
10 {
11     public:
12         // 在定义时使用父类定义
13         void getFileContent(fileParser*file)
14         {
15             // 针对抽象类编程,而不是具体类
16             file->parseContent();
17         }
18 };
19 
20 class XMLFileParser:public fileParser
21 {
22     void parseContent()
23     {
24         std::cout<<"Parse XML"<<std::endl;
25     }
26 };
27 
28 class jsonFileParse:public fileParser
29 {
30     void parseContent()
31     {
32         std::cout<<"Parse Json"<<std::endl;
33     }
34 };
35 
36 
37 int main()
38 {
39     service s;
40     XMLFileParser *file1 = new XMLFileParser();
41     jsonFileParse *file2 = new jsonFileParse();
42     // 使用子类实现
43     s.getFileContent(file1);
44     s.getFileContent(file2);
45     return 0;   
46 }

在上面这个例子中,我们在定义service的getFileContent接口时,使用抽象类fileParser作为形参,符合依赖倒转原则,在获取具体的文件内容时,使用具体类,符合里氏替换原则。如果需要新增其他的配置文件,只需要增加一个新的具体文件解析类,不用改动其他源代码,这也符合开闭原则。

在大多数情况下,这3个设计原则会同时出现,开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相互补充,相辅相成,目标一致,只是分析问题时所站角度不同而已。