面向对象编程(二)
-------面向对象的基本特性
1、面向对象的基本特性
面向对象的编程是把现实客观存在的事物进行归纳抽象,通过属性变量描述对象的某一方面的程度,通过函数方法描述对象的变化、行为、动作。对象是整个体系的中心,对象是由类构造来的。
这些基本概念不好理解,举个例子:现在我们要描述人,来先看看定义:人是会制造使用工具的灵长目人科人属的物种。通过层层概念剖析可以得到下图:
我们最终得到了人这个概念,可以通过这个概念区分其他物种或者物体。我们把这些特有属性和行为组合起来就可以得到一种分类,在编程的世界中就叫做类。这个世界有几十亿人,每个人是独一无二的,我们把每一个人称作人类的实例,人类是每一个人的抽象。我们在描述一个人personA的时候,用“人类”这个模板去初始化personA的一些属性,再当我们需要查询这个人的时候直接从personA的对象中找到他的属性。
类是编程领域的一个概念,要区别于现实世界中的类。为了描述一个概念,我们通常会从一些特性开始介绍。下面分别介绍下面向对象的四大基本特性:抽象、封装、继承、多态。很多书中并没有把抽象列为一大特性,笔者认为抽象非常重要,特此加上了。
2、抽象
良好的抽象策略可以控制问题的复杂程度,增强系统的通用性和可扩展性。抽象主要包括过程抽象和数据抽象。所谓过程抽象是将问题域中具有明确功能定义的操作抽取出来,并将其作为一个实体看待。数据抽象是将描述客体的属性和行为组合起来,从而达到对现实世界客体的模拟。
一个软件系统核心原型一般都可以抽象成几个客体对象和这些客体对象之间的关联。如选课系统主要就是描述的学生和课程之间的关系,关于怎么选课,怎么查询都可以抽象成方法,不同专业,不同学校的选课都肯有差异,这就是一种过程抽象。在这个选课活动中,我们只需要收录标识姓名ID 年级等信息,不需要关注学生的身高体重、籍贯、肤色;这可以理解是一种数据抽象。
3、封装
封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体。我们经常用到的有:类数据封装,函数方法封装。
访问控制:通过访问说明符private 、protected、public 实现不同程度的封装
举几个例子:
描述一个点,当在一条线上描述的时候,一个float就够了;但是如果在一个面上可能就需要两个float,如果业务增加条件变成三维空间你可能就要传3个数……。如果你的传递函数有很多层,那么恭喜你,你可能要花一天甚至很多天去修改,如果在一开始的时候封装了一个Position类,一些修改只需要在这个类中增加几个成员就好。
比如女生的年龄,很多18岁和38岁看起来差不多,当被问多少岁的时候,如果关系不会很好,女生可能会回答:关你什么事;如果关系一般,回答可能是:你猜!当你们关系很亲密的时候她才有可能呢return age。
比如人:在享受各种物质生活的和精神熏陶、思想表达的同时也要承受岁月的侵蚀。这句话中就包含了物质的输入、消化、输出;精神的输入、消化、输出;时间的递增。在外界的角度观察人,也许只需要关注个人的这些输入输出,外界需要给他一个时间信号。并不关注需要关注个人的消化过程。
从以上两点得出结论,封装的好处:
1、提高了代码的复用性和扩展性。
2、隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念。
4、继承
在面向对象编程中,继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等,如同上面的人类定义关系。
在面向对象系统的设计中,一般采用自顶向下逐步求精的方法。通常在设计业务关系原型的时候只会考虑主要基本的属性和关联。当有新业务扩展的时候再扩展之前设计的类。
继承是为了达到扩展(重新定义或者进化)之前设计的类,增加新的策略方法更好的嵌入旧的架构,高效复用代码的一种设计方法。
下表描述了三种继承关系的访问关系
5、多态
多态是过程抽象的一种具体应用。在很多时候描述主体关系时不需要去详细地描述每个细节关系,因为在某些场景我们是不需要关心那么多的,并且细节也有可能被优化。例如从广东到北京,封建时期可以是步行、骑马、坐船,可能要花好几个月;现在可以是飞机、高铁、汽车,一天就可以到。其实我们通常只是关心要到达目的地去干啥,比如进京赶考,进京参加两会。开会的内容或者开会的结果不会因为交通方式二改变。
多态可以分为动多态和静多态。动多态和静多态区分在于实现多态是在编译时还是运行时。如:在设计从广东到北京这个关系时候,具体是哪种运输方式,可能在执行运输的前一段时间才选择好,而不是在编译这个系统的时候就决定了。具体关系如下图:
静多态一般是采用模板<>尖括号实现的,在编译的时候将具体的类型作为参数,例如:在申明一个向量的时候,可以将float,int等传入<>中。这么做的好处是在进行代码设计的时候只需要关心对应的抽象关系,需要用到这种类型的时候再去具体法则关系。
vector<T> v1;