面向对象三大特性——继承高阶(接口和抽象类)
一、接口(Interface)
在C++、JAVA等程序开发时,往往会利用到接口。接口其实就是:自己提供给使用者来调用自己功能的方式\方法\入口。
=================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java /* * Java的Interface接口的特征: * 1)是一组功能的集合,而不是一个功能 * 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作 * 3)接口只定义函数,但不涉及函数实现 * 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */ package com.oo.demo; public interface IAnimal { public void eat(); public void run(); public void sleep(); public void speak(); } =================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口 package com.oo.demo; public class Pig implements IAnimal{ //如下每个函数都需要详细实现 public void eat(){ System.out.println("Pig like to eat grass"); } public void run(){ System.out.println("Pig run: front legs, back legs"); } public void sleep(){ System.out.println("Pig sleep 16 hours every day"); } public void speak(){ System.out.println("Pig can not speak"); } } =================第三部分:Person2.java /* *实现了IAnimal的“人”,有几点说明一下: * 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样 * 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */ package com.oo.demo; public class Person2 implements IAnimal { public void eat(){ System.out.println("Person like to eat meat"); } public void run(){ System.out.println("Person run: left leg, right leg"); } public void sleep(){ System.out.println("Person sleep 8 hours every dat"); } public void speak(){ System.out.println("Hellow world, I am a person"); } } =================第四部分:Tester03.java package com.oo.demo; public class Tester03 { public static void main(String[] args) { System.out.println("===This is a person==="); IAnimal person = new Person2(); person.eat(); person.run(); person.sleep(); person.speak(); System.out.println("\n===This is a pig==="); IAnimal pig = new Pig(); pig.eat(); pig.run(); pig.sleep(); pig.speak(); } } java中的interface
1、使用接口的好处
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这样做得好处也就是实现了归一化。
二、归一化
归一化:只要是基于同一个接口实现的类,那么这些类产生的对象在使用时,从用法来说是一样的。
1、归一化的好处
1)让使用者无需关心对象的类,只需要知道这些对象都具备某些功能即可,降低使用难度。
2)让高层的外部使用者可以不加区分地处理所有接口兼容的对象集合。
1、就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
2、再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
三、python中实现接口
在python中并没有interface关键字,如果想去模仿接口概念,有两种方法:
方法一:借助第三方模块 http://pypi.python.org/pypi/zope.interface
方法二:使用继承。
1、继承的两种用途
1)继承基类的方法,并做出自己的改变或扩展(代码重用)。
实际这种用法意义不大,甚至往往是有害的,因为它会使得子类和基类出现强耦合。
2)声明某个子类兼容于某基类,定义一个接口类,接口类中定义一些接口名(函数名)且并未实现接口功能,子类继承接口类,并实现接口的功能。又称为接口继承
# 正常实现调用 class Applepay: def pay(self,money): print('apple pay 支付了%s' %money) class Alipay: def pay(self,money): print('支付宝 支付了%s' %money) def payment(pay_obj,money): #实例化的另一种调用,这个方法让实例化的时候按照payment调用:就像下面的payment(apple1,200) pay_obj.pay(money) apple1 = Applepay() # apple1.pay(200) payment(apple1,200) """ apple pay 支付了200 """
# 2.有时候写的时候会把方法写错,自己定义一个主动报错 # 接口初成:手动报异常:NotImplementedError来解决开发中遇到的问题 class Payment: def pay(self): raise NotImplementedError #主动让程序报错 class Wechatpay(Payment): #微信支付 def pay(self,money): print('微信支付了%s元',money) class QQchatpay(Payment): #QQ支付 def fuqian(self,money): print('QQ支付了%s元',money) p = Wechatpay() p.pay(200) #不报错 # 微信支付了%s元 200 q = QQchatpay() q.pay() #报错
# 3.借用abc模块来实现接口 #接口类(就是为了提供标准,约束后面的子类) from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def pay(self,money): pass class Wechatpay(Payment): def fuqian(self,money): '''实现了pay的功能,但是方法名字不一样''' print('微信支付了%s元'%money) class Alipay: def pay(self,money): print('支付宝 支付了%s' %money) # p = Wechatpay() #报错了(因为上面定义了一个接口类,接口类里面 # 定义了一个pay方法,而在下面的Wechatpay方法里没有pay方法,不能 # 调用,在接口类里面约束一下,接口类里的pay方法里面不能写其他,直接pass) a = Alipay() a.pay(200) """ 支付宝 支付了200 """ # p = Payment() #接口类不能被实例化
由上面的例子可以归结,接口也就是做约束,让下面的类的方法都按照接口类中给出的方法去定义。如果接口类里有的方法类里面没有,则这个类不能被实例化。而abc模块的使用,则说明了接口和抽象类的关联性。
四、抽象类
1、抽象类概念
抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
2、抽象类特点
抽象类与普通类的特殊之处就是只能被继承(抽象类是不完整的,它只能用作基类),不能被实例化。
3、抽象类用处
在面向对象方法中,抽象类主要用来进行类型隐藏和充当全局变量的角色。降低使用者的使用复杂度,统一标准。
4、抽象类的实现
python的abc模块中定义了抽象类的metaclass类ABCMeta,以及抽象方法装饰器abstractmethod, abstractclassmethod, abstractstaticmethod,抽象property装饰器abstractproperty等。可以基于这些工具来实现自己的抽象类。
import abc #利用abc模块实现抽象类 class Animal(metaclass=abc.ABCMeta): # 类似接口,只定义规范,不实现具体的代码 @abc.abstractclassmethod def run(self): pass @abc.abstractclassmethod def eat(self): pass # 尝试实例化抽象类:结果失败 # animal = Animal() # TypeError: Can't instantiate abstract class Animal with abstract methods eat, run class People(Animal): def run(self): # 不符合规范,不允许实例化 print('people is walking') def eat(self): print('people is eating') class Pig(Animal): def run(self): print('Pig is running') def eat(self): print('Pig is eating') class Dog(Animal): def run(self): print('Dog is zouing') def eat(self): print('Dog is eating') peo1 = People() pig1 = Pig() Dog1 = Dog() peo1.eat() pig1.eat() Dog1.run() """ people is eating Pig is eating Dog is zouing """
#一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #这样大家都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
五、抽象类与接口
抽象类表示该类中可能已经有一些方法的具体定义,但是接口就仅仅只能定义各个方法的界面(方法名,参数列表,返回类型),并不关心具体细节。
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口之间的一个概念,同时具备类和接口的部分特性,可以用以实现归一化设计。
1、抽象类和接口相同点
2、包含未实现的方法声明;
3、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员)
2、不同点
1、类可以实现无限个接口,但仅能从一个抽象(或任何其他类型)类继承,从抽象类派生的类仍可实现接口,从而得出接口是用来解决多重继承问题的。
2、抽象类当中可以存在非抽象的方法,可接口不能。
3、抽象类中的成员变量可以被不同的修饰符来修饰,可接口中的成员变量默认的都是静态常量(static final)。
4、抽象类是对象的抽象,然而接口是一种行为规范。