面向对象程序设计与面向过程程序设计解析
昨天晚上在知乎看到这个问题,一时还真说不太清。之前一直用JAVA和Android做开发,近期在维护一个老的项目,是用VB开发的,代码超过十个年头了,接触了一段时间。对面向过程和面向对象都有所涉及,在这里这个小结(有些是在网上收集的)
自己的理解:
面向过程是一种以事件为中心的编程思想,以功能(行为)为导向,按模块化的设计,就是分析出解决这个问题所须要的步骤,然后用函数把这些步骤一步一步实现,实现的时候一个一个依次调用就能够了
面向对象是一种以事物为中心的编程思想,以数据(属性)为导向,将具有同样一个或者多个属性的物体抽象为“类”,将他们包装起来;而有了这些数据(属性)之后,我们再考虑他们的行为(对这些属性进行如何的操作),是把构成问题的事物分解成各个对象,建立对象的目的不是为了完毕一个步骤,而是为了描写叙述某个事物在整个解决这个问题的步骤中的行为。面向对象的技术,是一种以对象为基础,以事件或消息来驱动对象运行处理的程序设计技术。它具有封装性,继承性以及多态性。
知乎网友rlei;
面向对象编程强调“封装”,“继承“和“多态”。数据和与数据相关的操作被包装成对象(严格的说是“类”),每一种对象是相对完整和独立的。对象能够有派生的类型,派生的类型能够覆盖(或重载)原本已有的操作。全部的这些,是为了达成更好的内聚性,即一种对象做好一件(或者一类相关的)事情,对象内部的细节外面世界不关心也看不到;以及减少耦合性,即不同种类的对象之间相互的依赖尽可能减少。而全部的这些,都有助于达成一个崇高的目标,就是可复用性。别人写出来的东西,你能够简简单单拿过来用,还能够加以发展,这不是一个非常美好的世界吗?
精讲
面向过程就是分析出解决这个问题所须要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就能够了。面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完毕一个步骤,而是为了描叙某个事物在整个解决这个问题的步骤中的行为。
面向过程
面向过程的程序设计是一种自上而下的设计方法,设计者用一个main函数概括出整个应用程序须要做的事,而main函数由对一系列子函数的调用组成。对于main中的每个子函数,都又能够再被精炼成更小的函数。反复这个过程,就能够完毕一个过程式的设计。其特征是以函数为中心,用函数来作为划分程序的基本单位,数据在过程式设计中往往处于从属的位置。
面向过程式设计的长处是易于理解和掌握,这样的逐步细化问题的设计方法和大多数人的思维方式比較接近。
然而,过程式设计对于比較复杂的问题,或是在开发中需求变化比較多的时候,往往显得力不从心。这是由于过程式的设计是自上而下的,这要求设计者在一開始就要对须要解决的问题有一定的了解。在问题比較复杂的时候,要做到这一点会比較困难,而当开发中需求变化的时候,曾经对问题的理解或许会变得不再适用。其实,开发一个系统的过程往往也是一个对系统不断了解和学习的过程,而过程式的设计方法忽略了这一点。
在面向过程式设计的语言中,一般都既有定义数据的元素,如C语言中的结构,也有定义操作的元素,如C语言中的函数。这样做的结果是数据和操作被分离开,easy导致对一种数据的操作分布在整个程序的各个角落,而一个操作也可能会用到非常多种数据,在这样的情况下,对数据和操作的不论什么一部分进行改动都会变得非常困难。
在面向过程式设计中,main()函数处于一个非常重要的地位。设计者正是在main()函数中,对整个系统进行一个概括的描写叙述,再以此为起点,逐步细化出整个应用程序。然而,这样做的一个后果,是easy将一些较外延和易变化的逻辑(比方用户交互)同程序的核心逻辑混淆在一起。如果我们编写一个图形界面的计算器程序和一个命令行界面的计算器程序,能够想象这两个版本号的main()函数会有非常大的差异,由此衍生出来的程序非常有可能也会迥然不同,而这两个版本号的程序本应该有非常大部分能够共用才对。
面向过程式设计另一个问题就是其程序架构的依赖关系问题。一个典型的过程式程序往往如Figure 1所看到的:
![](http://java.chinaitlab.com/imgfiles/2005.11.4.10.8.56.11.1.jpg)
Figure 1
图中的箭头代表了函数间的调用关系,也是函数间的依赖关系。如图所看到的,main()函数依赖于其子函数,这些子函数又依赖于更小的子函数,而在程序中,越小的函数处理的往往是细节实现,这些详细的实现,又经常变化。这种结果,就是程序的核心逻辑依赖于外延的细节,程序中本来应该是比較稳定的核心逻辑,也由于依赖于易变化的部分,而变得不稳定起来,一个细节上的小小修改,也有可能在依赖关系上引发一系列变动。能够说这种依赖关系也是过程式设计不能非常优点理变化的原因之中的一个,而一个合理的依赖关系,应该是倒过来,由细节实现依赖于核心逻辑才对。
面向对象设计
面向对象是一种自下而上的程序设计方法。不像过程式设计那样一開始就要用main概括出整个程序,面向对象设计往往从问题的一部分着手,一点一点地构建出整个程序。面向对象设计以数据为中心,类作为表现数据的工具,是划分程序的基本单位。而函数在面向对象设计中成为了类的接口。
面向对象设计自下而上的特性,同意开发人员从问题的局部開始,在开发过程中逐步加深对系统的理解。这些新的理解以及开发中遇到的需求变化,都会再作用到系统开发本身,形成一种螺旋式的开发方式。(在这样的开发方式中,对于已有的代码,常须要运用Refactoring技术来做代码重构以体现系统的变化。)
和函数相比,数据应该是程序中更稳定的部分,比方,一个网上购物程序,不管怎么变化,大概都会处理货物、客户这些数据对象。只是在这里,仅仅有从抽象的角度来看,数据才是稳定的,假设考虑这些数据对象的详细实现,它们甚至比函数还要不稳定,由于在一个数据对象中增减字段在程序开发中是常事。因此,在以数据为中心构建程序的同一时候,我们须要一种手段来抽象地描写叙述数据,这样的手段就是使用函数。在面向对象设计中,类封装了数据,而类的成员函数作为其对外的接口,抽象地描写叙述了类。用类将数据和操作这些数据的函数放在一起,这能够说就是面向对象设计方法的本质。
在面向对象设计中类之间的关系有两种:客户(Client)关系和继承(Inheritance)关系。客户关系如Figure 2所看到的,表示一个类(Client)会使用到还有一个类(Server)。一般将这样的关系中的Client类称为client,Server类称为服务器。
![](http://java.chinaitlab.com/imgfiles/2005.11.4.10.10.42.11.2.jpg)
Figure 2
继承关系如Figure 3所看到的,表示一个类(Child)对还有一个类(Parent)的继承。一般将这样的关系中的Parent类称为父类,Child类称为子类。
![](http://java.chinaitlab.com/imgfiles/2005.11.4.10.10.53.11.3.jpg)
Figure 3
面向对象设计的过程就是将各个类按以上的两种关系组合在一起,这两种关系都很easy,只是组合在一起却能提供强大的设计能力。以下介绍怎样利用这两种关系来划分程序的不同模块。
在非常多应用中,我们都须要将数据存储到数据库中。一个常见的解决手段是使用分层的方法,将程序分为应用层和存储层,Figure 4中是这样的方法一个不太成熟的设计:
![](http://java.chinaitlab.com/imgfiles/2005.11.4.10.11.6.11.4.jpg)
Figure 4
Application代表应用层的逻辑,DB代表了数据库訪问的逻辑,在这个设计中,Application直接调用了DB的服务来将数据存储到数据库中。这样做的缺点是Application对DB有了依赖关系,一旦DB有了不论什么变化,Application都有可能会受其影响须要修改。当然,假设仅仅是两个类的话,这样的依赖关系根本算不上什么问题,然而,我们有理由相信,应用层和存储层都会由不仅仅一个类组成,并都有一定的复杂度,这时它们之间的这样的依赖关系就会让人头痛了。当程序的模块越来越多,假设不限制它们之间的联系,那模块间的依赖、程序的复杂度和开发维护的难度都会成指数上升。
所幸的是引入面向对象中的继承关系,我们能够解决这一问题,如图Figure 5所看到的:
![](http://java.chinaitlab.com/imgfiles/2005.11.4.10.11.28.11.5.jpg)
Figure 5
Persistence是一个接口,其包括了Application存储所需用到的服务。DB实现了这个接口,Application则调用Persistence中的服务来存储数据,两者都仅仅依赖于Persistence。这样,Application到DB的依赖关系被Persistence这个接口切断了。由于接口中仅仅包括了服务,也就是成员函数的声明,而不包括不论什么数据和函数的实现,所以Persistence接口的内容会非常easy。在Persistence不变的情况下,Application和DB这两个模块都能够自由地进行改动而不影响到对方。就这样,通过在中间插入接口的方法,程序的模块被划分开,依赖关系也变得容易管理的多。
假设从还有一个角度来看Figure 4中的设计,应用层是程序的核心部分,而存储层则能够看成是实现的细节,这个设计犯了和过程式设计相同的错误,即核心逻辑依赖于详细的实现细节,这样,当细节变化时,核心逻辑也会受到影响:假如,当我们须要将数据改存到文件或文件夹服务中时,依照Figure 4中的设计Application模块就难免受到影响。在Figure 5的设计中则没有这一问题:这里我们能够把Application和Persistence看成是程序的核心逻辑,而Persistence接口的实现(DB)能够看成是程序的细节实现,其依赖关系是细节依赖于核心,当细节变化时核心不会受其影响,这才是一个稳定的依赖关系。遵从这一设计,我们能够在不影响程序核心的情况下,为程序加入�或更改存储类型,如Figure 6所看到的:
![](http://java.chinaitlab.com/imgfiles/2005.11.4.10.11.38.11.6.jpg)
Figure 6
这里值得注意的是Persistence和Application被划分到了一起,这是由于Persistence中的服务都是依据Application中的需求制定出来的,Persistence和Application的关系比起它和其子类的关系更加紧密。将接口和其调用者放入一个模块是一个常见的做法,当然,也能够将接口单独划分为一个模块,这通常是在有多个不同的调用模块依赖于该接口,或不同模块间须要更清晰的分离时才这样做。