面向过程与面向对象 程序设计
一、面向过程的程序:
按照思路是下棋的流程进行编程, 也就是我们通常习惯使用的自顶向下顺序执行, 逐步求精的编程思想。其程序结构是按照功能划分成若干个基本模块,这些模块形成一个树状结构,各模块之间的关系也比较简单,在功能上相对独立,每一模块每部一般都是由顺序、选择和循环3种基本结构组成,其模块化实现的具体方式是使用子程序,而程序的流程在写程序时就已经决定。
这种编程的主导思想是在设计时就把实现的细节考虑好了, 从而在低层就实现了程序满足应用软件要求的目的。
二、面向对象的精髓:
在于考虑问题的思路是从人的一般思维习惯出发来考虑的, 这样就改变并打破了程序员以往的编程习惯, 而把人们解决日常问题的思维方法逐步翻译成程序能接受的抽象算法的过程, 使用面向对象的思维方法, 其实是一个将程序设计思维方式从具体的编程技术当中抽象出来的过程, 而这个抽象的过程是自上而下的, 较符合人类的思维习惯, 就是先不考虑问题解决的细节, 把问题的最主要的方面抽象成为一个简单的框架, 集中精力思考如何解决主要问题, 然后在解决问题的过程中, 再把问题的细节分割成一个一个小问题, 再专门去解决细节问题。因而一旦牢牢的抓住了这一 点, 我们就会发现在软件 设计和开发过程中, 总是会不知不觉的 运用面向对象的思维方法来设计和编写程序, 并且程序的设计 和开发也变得不再那么枯燥, 而一个合理运用面向对象技术进 行设计和架构的软件, 更 是具备了思维的艺术感。最终使软件系 统更可靠, 容易维护, 风格一致, 可 重用, 代码清晰。
概括来讲,面向对象与面向过程主要有以下几个方面的不同之处:
(1)出发点不同。面向对象方法是用符合常规的思维方式来处理客观世界的问题,强调把问题域的要领直接映射到对象以及对象之间的接口上。而面向过程方法强调的则是过程的抽象化与模块化,它是以过程为中心构造或处理客观世界问题的。
(2)层次逻辑关系不同。 面向对象在于考虑问题的思路是从人的一般思维习惯出发来考虑的, 这样就改变并打破了程序员以往的编程习惯, 而把人们解决日常问题的思维方法逐步翻译成程序能接受的抽象算法的过程, 使用面向对象的思维方法, 其实是一个将程序设计思维方式从具体的编程技术当中抽象出来的过程。而面向过程方法处理问题的基本单位是能清晰准确的表达过程的模块,用模块的层次结构概括模块或模块间的关系与功能,把客观世界的问题抽象成计算机可以处理的过程。
(3)数据的处理方式与控制方式不同。面向对象方法将数据与对应的代码封装成一个整体,原则上其他对象不能直接修改其数据,即对象的修改只能由自身的成员函数完成,控制程序方式上是通过“事件驱动”来激活和运行程序。而面向过程方法是直接通过处理程序来处理数据,处理完毕后即可显示处理结果,在控制程序方式上是按照设计调用或返回程序,不能自由导航,各个模块之间存在控制与被控制、调用与被调用的关系。
(4)分析设计与编码的转换方式不同。面向对象方法贯穿于软件生命周期的分析、设计及编码中,是一种平滑过程,从分析到设计再到编码是采用一致性的模型表示,即实现的是一种无缝连接。而面向过程方法强调分析、设计及编码之间按规则进行转换,贯穿于软件生命周期的分析、设计及编码中,实现的是一种有缝的连接。
三、面向对象与面向过程各自的优缺点:
面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
四、面向对象程序设计的一些显著的特性包括:
.程序设计的重点在于数据而不是过程;
面向对象的主要特征包括抽象、继承、封装和多态。
.程序被划分为所谓的对象;
.数据结构为表现对象的特性而设计;
.函数作为对某个对象数据的操作,与数据结构紧密的结合在一起;
.数据被隐藏起来,不能为外部函数访问;
.对象之间可以通过函数沟通;
.新的数据和函数可以在需要的时候轻而易举的添加进来;
.在程序设计过程中遵循由下至上(bottom-up)的设计方法。
面向对象程序设计是一种方法,这种方法为数据和函数提供共同的独立内存空间,这些数据和函数可以作为模板以便在需要时创建类似模块的拷贝。
一个对象被认为是计算机内存中的一个独立区间,在这个区间中保存着数据和能够访问数据的一组操作。因为内存区间是相互独立的,所以对象可以不经修改就应用于多个不同的程序中。
通常,在面向对象的程序设计风格中,你会将一个问题分解为一些相互关联的子集(相当于若干个if语句),每个子集内部都包含了相关的数据(相当于if语句中的条件)和函数(相当于if语句中的执行代码)。同时,你会以某种方式将这些子集分为不同等级(相当于if语句中的嵌套),而一个对象就是已定义的某个类型的变量。当你定义了一个对象,你就隐含的创建了一个新的数据类型。(不是很理解???)
一旦定义了一个类,我们就可以创建这个类的多个对象,每个对象与一组数据相关,而这组数据的类型在类中定义。因此,一个类就是具有相同类型的对象的抽象。
把数据和函数包装在一个单独的单元(称为类)的行为称为封装。数据不能被外界访问,只能被封装在同一个类中的函数访问。这些函数提供了对象数据和程序之间的接口。
简单的说,一个对象就是一个封装了数据和操作这些数据的代码的逻辑实体。
(补充)1.抽象:
抽象——就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。(就是把现实世界中的某一类东西,提取出来,用程序代码表示,抽象出来一般叫做类或者接口。)抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是数据抽象,二是过程抽象。
数据抽象——就是用代码的形式表示现时世界中一类事物的特性,就是针对对象的属性。比如建立一个鸟这样的类,鸟都有以下属性:一对翅膀、两只脚、羽毛等。抽象出来的类都是鸟的属性,或者成员变量。
过程抽象——就是用代码形式表示现实世界中事物的一系列行为,就是针对对象的行为特征。比如鸟会飞、会叫等。抽象出来的类一般都是鸟的方法。
2.继承:
继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。因此可以说,继承是为了重用父类代码,同时为实现多态性作准备。
3.封装:
封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提下改变类的内部结构,同时保护了数据。
4. 多态性:
多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性(重载、继承、实现)。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。总的来说,方法的重写、重载与动态链接构成多态性。Java引入多态的概念原因之一就是弥补类的单继承带来的功能不足。(为规避C++中多继承造成的复杂继承问题,java采用单继承。)
动态链接——对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将会调用子类中的这个方法,这就是动态链接。
注意:继承与重载:一是子类与父类的关系,二是重载方法的调用问题。
子类对象可以直接当成父类对象使用,但反过来就不可以。举例来说,人是父类,学生是人的子类,所以学生对象一定具备人对象的属性,但是人对象就未必具有学生对象的特性。所以学生对象可以当做人对象来使用,但是人对象就不能当做学生对象使用。注意当把子类对象当成父类对象使用时,子类对象将失去所有的子类特性,只保留与父类同名的属性和方法(同名方法不仅是函数名相同,而且参数类型也要一样,否则不予保留)。此时可以对父类方法进行重写。
一个类中如果定义了重载的方法,则系统在调用方法时,会根据参数的类型自动选择调用合适的方法。
五、面向对象的好处:
1、易维护采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。2、质量高在设计时,可重用现有的,在以前的项目的领域中已被测试过的类使系统满足业务需求并具有较高的质量。3、效率高在软件开发时,根据设计的需要对现实世界的事物进行抽象,产生类。使用这样的方法解决问题,接近于日常生活和自然的思考方式,势必提高软件开发的效率和质量。4、易扩展由于继承、封装、多态的特性,自然设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低。
五、面向对象与面向过程的生动比喻:
用面向过程的方法写出来的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭。所谓盖浇饭,北京叫盖饭,东北叫烩饭,广东叫碟头饭,就是在一碗白米饭上面浇上一份盖菜,你喜欢什么菜,你就浇上什么菜。我觉得这个比喻还是比较贴切的。
蛋炒饭制作的细节,我不太清楚,因为我没当过厨师,也不会做饭,但最后的一道工序肯定是把米饭和鸡蛋混在一起炒匀。盖浇饭呢,则是把米饭和盖菜分别做好,你如果要一份红烧肉盖饭呢,就给你浇一份红烧肉;如果要一份青椒土豆盖浇饭,就给浇一份青椒土豆丝。
蛋炒饭的好处就是入味均匀,吃起来香。如果恰巧你不爱吃鸡蛋,只爱吃青菜的话,那么唯一的办法就是全部倒掉,重新做一份青菜炒饭了。盖浇饭就没这么多麻烦,你只需要把上面的盖菜拨掉,更换一份盖菜就可以了。盖浇饭的缺点是入味不均,可能没有蛋炒饭那么香。
到底是蛋炒饭好还是盖浇饭好呢?其实这类问题都很难回答,非要比个上下高低的话,就必须设定一个场景,否则只能说是各有所长。如果大家都不是美食家,没那么多讲究,那么从饭馆角度来讲的话,做盖浇饭显然比蛋炒饭更有优势,他可以组合出来任意多的组合,而且不会浪费。
盖浇饭的好处就是"菜""饭"分离,从而提高了制作盖浇饭的灵活性。饭不满意就换饭,菜不满意换菜。用软件工程的专业术语就是"可维护性"比较好,"饭" 和"菜"的耦合度比较低。蛋炒饭将"蛋""饭"搅和在一起,想换"蛋""饭"中任何一种都很困难,耦合度很高,以至于"可维护性"比较差。软件工程追求的目标之一就是可维护性,可维护性主要表现在3个方面:可理解性、可测试性和可修改性。面向对象的好处之一就是显著的改善了软件系统的可维护性。
额外补充内容:
面向对象设计的原则
SRP 单一职责原则
就一个类而言,应该仅有一个引起它变化的原因。
OCP 开放————封闭原则
软件实体(类、模块、函数等)应该是可以开展的,但是不可修改。
LSP Liskov 替换原则
子类型必须能够替换掉它们的父类型。
DIP 依赖倒置原则
抽象不应该依赖于细节。细节应该依赖于抽象。
ISP 接口隔离原则
不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。
REP 重用发布等价原则
重用的粒度就是发布的粒度
CCP 共同封闭原则
包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他的包不造成任何影响。
CRP 共同重用原则
一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类。
ADP 无环依赖原则
在包的依赖关系图中不允许存在环。
SDP 稳定依赖原则
朝着稳定的方向进行依赖
SAP 稳定抽象原则
包的抽象程度应该和其稳定程度一致。