本文从代码的重构谈了面向对象中的多态,及多态性的前提与好处。先记住这句话:多态的真正含义是:使用指向父类的指针或引用,能够调用子类的对象。
-
先观察代码的重构
class Animal:
def talk(self):
print('makeNoise') # 动物都能发出声音
def eat(self): # 动物会吃
print('eat')
def sleep(self): # 动物会睡觉
print('sleep')
class Dog(Animal):
def talk(self):
print('Dog...barking') # 狗汪汪
def eat(self):
print('Dog eating bone')
def sleep(self):
print('Dog sleeping')
class Pig(Animal):
def talk(self):
print('Pig...hem') # 猪哼哼
def eat(self):
print('Pig eating qingCai')
def sleep(self):
print('Pig sleeping')
def use_dog(dog):
dog.talk()
dog.eat()
dog.sleep()
# 我要开始养狗了
# 第一只
p1 = Dog()
p1.talk()
p1.eat()
p1.sleep()
# 第二只
p2 = Dog()
p2.talk()
p2.eat()
p2.sleep()
# 开始养猪
# 第一只
p1 = Pig()
p1.talk()
p1.eat()
p1.sleep()
# 第二只
p2 = Pig()
p2.talk()
p2.eat()
p2.sleep()
思考:狗养的越多,对象.方法() 调用越多
-
观察红框中的代码,你不觉得很相似吗?仅对象名不一样,我们准备用方法(函数)改进
class Animal:
def talk(self):
print('makeNoise') # 动物都能发出声音
def eat(self): # 动物会吃
print('eat')
def sleep(self): # 动物会发声
print('sleep')
class Dog(Animal):
def talk(self):
print('Dog...barking') # 狗汪汪
def eat(self):
print('Dog eating bone')
def sleep(self):
print('Dog sleeping')
class Pig(Animal):
def talk(self):
print('Pig...hem') # 猪哼哼
def eat(self):
print('Pig eating qingCai')
def sleep(self):
print('Pig sleeping')
'''
以下为改进代码
'''
# 用函数改进
def use_dog(dog):
dog.talk()
dog.eat()
dog.sleep()
# 同样的对cat一样
def use_pig(pig):
pig.talk()
pig.eat()
pig.sleep()
# 我要开始养狗了
# 第一只
d1 = Dog()
use_dog(d1)
# 第二只
d2 = Dog()
use_dog(d2)
# 开始养猪
# 第一只
p1 = Pig()
use_pig(p1)
# 第二只
p2 = Pig()
use_pig(p2)
-
调用变的简洁
-
仔细观察下图中红框中的代码,仅对象名不一样。
-
再次重构
# 类的定义与上面的代码一样
'''
以下为改进代码
'''
def use_animal(animal):
animal.talk()
animal.eat()
animal.sleep()
# 我要开始养狗了
# 第一只
d1 = Dog()
use_animal(d1)
# 第二只
d2 = Dog()
use_animal(d2)
# 开始养猪
# 第一只
p1 = Pig()
use_animal(p1)
# 第二只
p2 = Pig()
use_animal(p2)
-
将每种动物的功能用use_animal统一起来。
从上面示例中可以看到,Dog、Pig是Animal的二个子类,在调用函数的时候,参数指定为Animal类型,但具体传入的是Anima的子类的对象Dog(d1, d2)、Pig(p1, p2)。
当不同(Dog、Pig)去完成某个行为(talk、eat、sleep)时,会产生出不同的状态(狗有狗爱吃的骨头,猫有猫爱吃的青菜), 这就是多态。
多态性的前提:
- 继承关系 例如: class Dog(Animal):
- 方法重写 例如:def talk(self): print('Dog...barking') # 狗汪汪
- 父类引用指向子类对象 例如:use_animal(p1), 实际上定义时参数animal 的类型应该为Animal 类型。
- 假如我现在想养一只狐狸(Fox), 我仅需要添加Fox类的定义 ,然后调用use_animal(狐狸对象)
# 狐狸类定义
class Fox(Animal):
def talk(self):
print('Fox...whaaa') # 狐狸哦哇
def eat(self):
print('Fox eating grape')
def sleep(self):
print('Fox sleeping')
'''
以下为改进代码
'''
def use_animal(animal):
animal.talk()
animal.eat()
animal.sleep()
# 开始养狐狸
f1 = Fox()
use_animal(f1)
f2 = Fox()
use_animal(f2)
从上面代码的重过过程已偿到多态的好处:
- 提高了代码的维护性
- 提高了代码的扩展性
可以比较使用多态和不使用多态,很显然使用多态代码更清晰和简洁。除此之外,当我们增加新的子类(Fox、Cock)的时候无须变动代码就能适用,如果不使用多态,就需要添加use_fox,use_cock,use_xxx,...等方法。