最近阅读到了一篇关于面向对象编程和面向过程编程解释说明的文章,觉得写的通俗易懂。文章链接如下:
漫画:如何通俗易懂地解释面向对象思想?
但是该文章举的例子是用Java,故在此我按照我的理解,针对python,梳理一下面向对象编程。
一、面向对象编程背景和定义
维基百科:面向对象程序设计
1.背景
面向对象程序设计的雏形,早在1960年代的Simula语言中即可发现,当时的程序设计领域正面临着一种危机:在软硬件环境逐渐复杂的情况下,软件如何得到良好的维护?面向对象程序设计在某种程度上通过强调可重复性解决了这一问题。
2.定义
面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、特性、代码与方法。对象则指的是类(class)的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。
从维基百科的定义可以看出,面向对象编程的对象在Python中指的是类(class)的实例。
二、面向对象编程和面向过程编程
看上面对于面向对象编程的定义,感觉对面向对象编程还是很模糊。下面根据参考文章中的例子,对比面向过程编程来对面向对象编程进行进一步说明。
情景说明:
1.某商店需要用代码完成输入客人购买商品的数量和单价,返回客人购买商品的总价格。
由于代码比较简单,面向对象和面向过程编程得到的代码一样,如下。
class Bill(object):
def __init__(self, unit, number):
self.unit = unit
self.number = number
self.price = self.unit * self.number
def getPrice(self):
return self.price
if __name__ == '__main__':
unit = 10
number = 2
demo = Bill(unit, number)
print(demo.getPrice())
从这个情境中难以看出面向对象和面向过程的区别。别急,接下来在1的基础上增加需求。
2.该商店七夕大酬宾,七夕节当天所有商品均打77折
面向过程编程结果如下:
class Bill(object):
def __init__(self, unit, number, todayIsLoversDay):
self.unit = unit
self.number = number
self.price = self.unit * self.number
self.todayIsLoversDay = todayIsLoversDay
def getPrice(self):
if self.todayIsLoversDay:
return self.price * 0.77
return price
if __name__ == '__main__':
unit = 10
number = 2
todayIsLoversDay = True
demo = Bill(unit, number, todayIsLoversDay)
print(demo.getPrice())
因为面向对象编程主要关注于对象,故新建一个类,但是因为新的类需要用到Bill类中的unit、number、price,故要继承Bill类。
面向对象编程如下:
class Bill(object):
def __init__(self, unit, number):
self.unit = unit
self.number = number
self.price = self.unit * self.number
def getPrice(self):
return self.price
class LoversDayBill(Bill):
def __init__(self, unit, number):
super(LoversDayBill, self).__init__(unit, number)
def discount(self):
return self.price * 0.77
if __name__ == '__main__':
unit = 10
number = 2
demo = LoversDayBill(unit, number)
print(demo.discount())
从这个场景不难看出,面向对象对于代码的改动较多,面向过程编程只是添加了一个if语句,而面向对象编程则直接写了一个新的类,只不过这个类是继承Bill类。
那么是不是面向对象编程就比面向过程编程复杂呢?我们再在情景2的基础上增加需求。
3.增加需求,中秋节满399减100,国庆节100元以内的有1/10概率免单。
面向过程编程一如既往在getPrice中添加if语句,但此时已经显得比较复杂了,无论是新增或删除代码,在这个过长的类里做修改都是件不太愉快的事。为了在一个很长的函数中找到需要修改的位置,「面向过程」使得老过不得不浏览大量与修改无关的代码,小心翼翼地修改后,又要反复确认不会影响到类的其他部分。
面向对象编程和情景2一样,因为都是对价格进行处理,故这次新写了两个类分别处理中秋节和国庆节,这两个类都继承了Bill类。
应对新需求时,无需改变经测试通过的既有的类。它良好的灵活性和扩展性仿佛天上就是用来适应变化的。每个支付方式相互独立,假设若某一天不再需要时,将对应的类删除即可。
4.修改需求,情景2中情人节活动更改为凡是到本店来购物的情侣满99元及以上的订单随机附赠精美礼品一份【鲜花、巧克力、9.9元优惠卷】,之前打77着的活动也修改为仅限情侣参加。
这次面向过程编程明显要在getPrice函数中的关于情人节的if语句进行修改,而面向对象编程则只需要找到LoversDayBill
这个类,修改这个类即可。
从这4个场景不难看出面向对象和面向过程的编程思想是如何实现的。面向过程编程始终在修改Bill类,而面向对象编程始终是添加新的类(继承Bill类)
面向对象的编程方式使得每一个类都只做一件事。面向过程会让一个类越来越全能,就像一个管家一样做了所有的事。 而面向对象像是雇佣了一群职员,每个人做一件小事,各司其职,最终合作共赢!
三、引申阅读
《大话设计模式》中大鸟给小菜讲的故事非常经典:
“话说三国时期,曹操带领百万大军攻打东吴,大军在长江赤壁驻扎,军船连成一片,眼看就要灭掉东吴,统一天下,曹操大悦,于是大宴众文武,在酒席间,曹操诗性大发,不觉吟道:‘喝酒唱歌,人生真爽……’众文武齐呼:‘丞相好诗!’于是一臣子速命印刷工匠刻版印刷,以便流传天下。”
“样张出来给曹操一看,曹操感觉不妥,说道:‘喝与唱,此话过俗,应改为‘对酒当歌’较好!’于是此臣就命工匠重新来过。工匠眼看连夜刻版之工,彻底白费,心中叫苦不迭。只得照办。”
“样张再次出来请曹操过目,曹操细细一品,觉得还是不好,说:‘人生真爽‘太过直接,应改问语才够意境,因此应改为‘对酒当歌,人生几何……’当臣子转告工匠之时,工匠晕倒……”
大鸟:“小菜你说,这里面问题出在哪里?”
小菜:“是不是因为三国时期活字印刷还未发明,所以要改字的时候,就必须要整个刻板全部重新刻。”
大鸟:“说得好!如果是有了活字印刷,则只需更改四个字就可,其余工作都未白做。岂不妙哉。
一、要改,只需更改要改之字,此为可维护;
二、这些字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用;
三、此诗若要加字,只需另刻字加入即可,这是可扩展;
四、字的排列其实可能是竖排可能是横排,此时只需将活字移动就可做到满足排列需求,此是灵活性好。”
“而在活字印刷术出现之前,上面的四种特性都无法满足,要修改,必须重刻,要加字,必须重刻,要重新排列,必须重刻,印完这本书后,此版已无任何可再利用价值。”
小菜:“是的,小时候我一直奇怪,为何火药、指南针、造纸术都是从无到有,从未知到发现的伟大发明,而活字印刷仅仅是从刻版印刷到活字印刷的一次技术上的进步,为何不是评印刷术为四大发明之一呢?原来活字印刷是思想的成功,面向对象的胜利。”
四、面向对象的基本特征
面向对象的有三个基本特征:封装、继承、多态。
封装: 我的理解就是把一个东西用类表示,就像Bill类
继承: 我的理解就是子类有一部分父类以及写好,直接把父类拿来用就像。就像loversDayBill类中的price。
多态: 我的理解就是多个子类运行用相同的方法名。
关于python类的继承、多态请参考:
一文看懂Python面向对象编程
什么是多态,Python多态及用法详解