Python接口与归一化设计

1.什么是接口(interface)

    接口(interface)是面向对象编程语言中接口操作的关键字,功能是把所需成员组合起来,用来装封一定功能的集合。它好比一个模板,在其中定义了对象必须实现的成员,通过类或结构来实现它。接口不能直接实例化,接口不能包含成员的任何代码,只定义成员本身。接口成员的具体代码由实现接口的类提供。

2.归一化

    使用接口的意义在于归一化,什么叫归一化:就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。归一化的好处在于:

  1. 归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
  2. 归一化使得上层的外部使用者可以不加区分的处理所有接口兼容的对象集合
  • 1. 就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
  • 2. 再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样

3.Python中的interface

    首先先看一个Java代码的接口实例:

 1 // Java 语言中的接口很好的展现了接口的含义: IAnimal.java
 2 
 3 /*
 4 
 5 * Java的Interface很好的体现了我们前面分析的接口的特征:
 6 
 7 * 1)是一组功能的集合,而不是一个功能
 8 
 9 * 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作
10 
11 * 3)接口只定义函数,但不涉及函数实现
12 
13 * 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了
14  
15 */
16 /*    Animal接口    */
17 package com.oo.demo;
18  
19 public interface IAnimal {
20     public void eat(); 
21     public void run();
22     public void sleep();
23     public void speak();
24 }
25 //========== Pig.java
26 package com.oo.demo;
27  
28 public class Pig implements IAnimal{ //如下每个函数都需要详细实现
29     public void eat(){
30         System.out.println("Pig like to eat grass");
31     }
32  
33   
34     public void run(){
35         System.out.println("Pig run: front legs, back legs");
36     }
37  
38   
39     public void sleep(){ 
40         System.out.println("Pig sleep 16 hours every day");
41     }
42  
43   
44     public void speak(){ 
45         System.out.println("Pig can not speak"); 
46     }
47 }
48 
49 //=============== Person.java
50 package com.oo.demo;
51  
52 public class Person2 implements IAnimal {
53     public void eat(){ 
54         System.out.println("Person like to eat meat"); 
55     }
56  
57     public void run(){
58  
59         System.out.println("Person run: left leg, right leg");
60  
61     }  
62  
63     public void sleep(){ 
64         System.out.println("Person sleep 8 hours every dat");
65     }
66  
67   
68     public void speak(){
69         System.out.println("Hellow world, I am a person");
70     }
71 }
View Code

         在python中根本就没有一个叫做interface的关键字,可以使用抽象类似的效果:

3.1 抽象类

    与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。

    如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。比如:我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。你永远无法吃到一个叫做水果的东西。

  • 从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
  • 从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的。

3.2 抽象类与接口

  抽象类本质还是类,指的是一组类的属性,包括数据属性(如all_type)和方法属性(如read、write),而接口只强调函数属性的相似性。抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计。

3.3 在Python中实现抽象类

3.3.1 ABC模块

  Python有一个模块:ABC,Abstract Base Class(抽象基类),主要定义了基本类和最基本的抽象方法,可以为子类定义共有的API,不需要具体实现。相当于是Java中的接口或者是抽象类。首先看一个ABC的使用案例:

 1 import abc  # 利用abc模块实现抽象类
 2 
 3 
 4 class AllFile(metaclass=abc.ABCMeta):
 5     all_type = 'file'
 6 
 7     @abc.abstractmethod  # 定义抽象方法,无需实现功能
 8     def read(self):
 9         """子类必须定义读功能"""
10         pass
11 
12     @abc.abstractmethod  # 定义抽象方法,无需实现功能
13     def write(self):
14         """子类必须定义写功能"""
15         pass
16 
17 
18 class Txt(AllFile):
19     pass
20 
21 
22 t1 = Txt()  # 报错,子类没有定义抽象方法

 运行结果如下:

Traceback (most recent call last):
  File "E:/workspace/py3demo/02.py", line 23, in <module>
    t1 = Txt()  # 报错,子类没有定义抽象方法
TypeError: Can't instantiate abstract class Txt with abstract methods read, write

 发现运行出现错误“Can't instantiate abstract class Txt with abstract methods read, write”,意思大致是“不能对抽象类Txt的抽象方法read、write进行实例化”!

重新来设计:

 1 import abc  # 利用abc模块实现抽象类
 2 
 3 
 4 class AllFile(metaclass=abc.ABCMeta):
 5     all_type = 'file'
 6 
 7     @abc.abstractmethod  # 定义抽象方法,无需实现功能
 8     def read(self):
 9         """子类必须定义读功能"""
10         pass
11 
12     @abc.abstractmethod  # 定义抽象方法,无需实现功能
13     def write(self):
14         """子类必须定义写功能"""
15         pass
16 
17 
18 class Txt(AllFile):  # 子类继承抽象类,但是必须定义read和write方法
19     def read(self):
20         print('文本数据的读取方法')
21 
22     def write(self):
23         print('文本数据的读取方法')
24 
25 
26 class Sata(AllFile):  # 子类继承抽象类,但是必须定义read和write方法
27     def read(self):
28         print('硬盘数据的读取方法')
29 
30     def write(self):
31         print('硬盘数据的读取方法')
32 
33 
34 class Process(AllFile):  # 子类继承抽象类,但是必须定义read和write方法
35     def read(self):
36         print('进程数据的读取方法')
37 
38     def write(self):
39         print('进程数据的读取方法')
40 
41 
42 wenbenwenjian = Txt()
43 
44 yingpanwenjian = Sata()
45 
46 jinchengwenjian = Process()
47 
48 # 这样大家都是被归一化了,也就是一切皆文件的思想
49 wenbenwenjian.read()
50 yingpanwenjian.write()
51 jinchengwenjian.read()
52 
53 print(wenbenwenjian.all_type)
54 print(yingpanwenjian.all_type)
55 print(jinchengwenjian.all_type)

 运行结果如下:

文本数据的读取方法
硬盘数据的读取方法
进程数据的读取方法
file
file
file
 3.4.2 抛出异常

可以在抽象类的抽象方法中抛出异常,若派生类中重新实现了该抽象方法,则将会覆盖掉派生类中的派生方法,否则将会抛出异常:

 1 class AllFile(object):
 2     all_type = 'file'
 3 
 4     def read(self):
 5         """子类必须定义读功能"""
 6         raise NotImplementedError('未实现该方法!')
 7 
 8     def write(self):
 9         """子类必须定义写功能"""
10         raise NotImplementedError('未实现该方法!')
11 
12 
13 class Txt(AllFile):
14     pass
15 
16 
17 t1 = Txt()  # 报错,子类没有定义抽象方法
18 t1.write()

 运行程序,结果如下:

raise NotImplementedError('未实现该方法!')
NotImplementedError: 未实现该方法!

 可以看出,继承于抽象类AllFile的txt在未实现write方法时,抛出了NotImplementedError异常,同样达到了限制的功能。

 本文参考:

    lynnyq:https://blog.csdn.net/lynnyq/article/details/79560867