电子说
周立功教授数年之心血之作《程序设计与数据结构》以及《面向AMetal框架与接口的编程(上)》,电子版已无偿性分享到电子工程师与高校群体,书本内容公开后,在电子行业掀起一片学习热潮。经周立功教授授权,本公众号特对《程序设计与数据结构》一书内容进行连载,愿共勉之。
第四章为面向对象编程,本文为4.1OO思想。
第四章导读
面向过程编程(Process-Oriented Programming,POP)是一种以过程为中心的编程思想,以正在发生的事件为主要目标,指导开发者利用算法作为基本构建块构建复杂系统。
面向对象编程OOP(Object-Oriented Programming,OOP)是利用类和对象作为基本构建块,指导开发者探索基于对象和面向对象编程语言的表现力。因此分解系统时,要么从算法开始,要么从对象开始,然后利用得到的结构作为框架构建系统。
>>> 4.1 OO思想
>>> 4.1.1 职责转移
1、人脑的限制
由于受到电脑的信息处理功能的影响,因此于20世纪60年代初产生了以信息处理论为基础的认知心理学。美国心理学家乔治•米勒在信息记忆上的研究成就,为新兴的认知心理学提供了理论的证据。
虽然当时的心理学家已将信息处理的历程,大致区分为感官记忆(2秒以下)、短时记忆(15秒以下)与长时记忆,但短时记忆的性质及其重要性,则是在乔治•米勒1956 年发表研究报告《神奇的数字7 +/- 2,我们信息加工能力的局限》之后才被确定的,即米勒魔术——人类只能记住和处理7加或减2项内容。
后来的证据表明基数可能少到3或4,这个数字代表大脑“暂存器”解决问题时所能保存的信息容量。无论实际数目是多少,如果要求普通人同时考虑大约15件事情,实际上最多只能记住和处理其中9件甚至更少。
如果要求处理的事情更多,一次只有几件可以同时处理,其它的会被快速切入或切出暂存器。想一想去商店采购15件东西,如果没有一份购物清单,你很可能漏掉东西或买回来的东西数量不正确。同样的道理,如果需求列表或产品清单中的事项成千上万,那么你的大脑根本没办法处理这样复杂的事情,除非将它分解成更小的结构化分组。
2、核心域和非核心域
一个软件系统封装了若干领域的知识,其中一个领域知识代表了系统的核心竞争力,这个领域被称为“核心域”,其它领域称为“非核心域”。虽然更通俗的说法是“业务”和“技术”,但使用“核心域”和“非核心域”更严谨。
非核心域就是别人的领域,比如,底层驱动、操作系统和组件,即便你有一些优势,那也是暂时的,竞争对手也能通过其它渠道获得。非核心域的改进是必要的,但不充分,还是要在核心域上深入挖掘,让竞争对手无法轻易从第三方获得。因为在核心域上深入挖掘,达到基于核心域的复用,这是获得和保持竞争力的根本手段。
要达到基于核心域的复用,有必要将核心域和非核心域分开考虑。因为人脑的容量是有限的,而过早地将各个领域的知识混杂,会增加不必要的负担,从而导致开发人员腾不出脑力思考核心域中更深刻的问题。
正因为人脑的容量和运算能力有限,待解决的问题的规模一旦变大,就必须分而治之,因为核心域与非核心域的知识都是独立的。比如,一个计算器要做到没有漏洞,其中的问题也很复杂。如果不使用状态图对领域逻辑显式地建模,再根据模型映射到实现。而是直接下手编程,领域逻辑的知识靠临时去想,最终得到的代码肯定破绽百出。其实有利润的系统,其内部都是很复杂的,千万不要幼稚地认为“我的系统不复杂”。
3、职责转移
在面向过程编程时,由于主程序承担的责任太多,要确保一切正确工作,还要协调各个函数并控制它们的先后顺序,因此经常会产生非常复杂的代码。很多时候变化是不可避免的,而功能分解法却又无法应对可能出现的变化。一旦修改代码,则bug越来越多。更重要的是,由于人类的大脑无法做太多复杂的处理,记忆力和理解力也是有限的。因此面对复杂的软件开发时,主程序不能做太多的事情,必须通过“分离关注点”进行职转移责。
假设要乘出租车去机场,一种方式是告诉司机,按照“启动、右转、左转、停止”等单独的接口去机场。这种方式需要乘客对自己的行为负责,乘客知道每个城市去机场的路线。
既然用户的需求总是在变化之中,我们将无法阻止变化。与其抱怨变化,不如改变开发过程,从而更有效地应对变化,面向对象编程就是这样作为对抗软件复杂性的手段出现的。
在面向对象编程时,另一种方式是告诉司机,“请载我去机场”。尽管具体实现在广州、北京或上海等不同城市中是不同的,但在任何城市都可以这么说。因为司机知道怎么去机场,司机对自己的行为负责,并信任司机知道如何执行,这就是职责转移,显然这种方法比功能分解法要容易得多。
由于每个对象都对自己的行为负责,因此必须有方法告诉对象要做什么。而方法都被标识为能够被其它对象调用,这些方法的集合被称为对象的公开接口。其形象的比喻为,“将软件对象看成具有某种职责的人,他要与其他人协作完成工作。”
>>> 4.1.2 OO机制
面向对象的编程是由类(class)这种结构实现的,C++类的概念是对C结构概念扩展。因此表面上看起来这些特征与面向对象编程语言有很大的关联,但实际上能用任何一种语言实现。不管实现语言如何,任何大的软件系统都会以某种形式使用抽象、继承或多态性。
1、封装
封装是OO方法中的一个重要原则,其含义是将封装视为任何形式的隐藏,对外形成一个边界,只暴露有限的对外接口使之与外部发生联系。封装不仅仅是将对象的全部属性和全部操作结合在一起,形成一个不可分割的独立单位(对象),而是发现变化将其封装。
抽象实现封装的分析工具,抽象可以使我们专注于应用程序最本质的方面,同时忽略细节。在确定如何实现功能之前,先关注对象是什么?做了什么?更具体地,抽象是将一类对象的共同特征总结出来创建类的过程,包括数据抽象和行为抽象。
数据抽象是数据和处理方法的结合,即封装数据和函数到类中的能力,因此又将数据抽象称为信息隐藏或封装,信息隐藏是一种软件设计思想。由于不必知道内部结构,因此可以将数据当作黑盒子来操作。即使将来数据结构发生变化,对外部也没有影响。从而避免程序的各个组成部分过于相互依赖,否则很小的变化也会引起巨大的连锁反应。
在结构化的设计中,通常将代码封装到函数和模块中。因此封装不是OO语言所特有的,但它能将数据结构和行为组织在一个实体中,其主要目的是为使用代码的程序员提供一致的接口,这是面向对象编程的突出特点。尽管如此,面向对象和结构化的代码并不是互斥的,实际上不用结构化代码将无法创建对象。因此在构建面向对象的系统时,在设计中依然离不开结构化的技术。
2、继承
虽然结构化程序设计通过编写一个功能块实现重用,但面向对象程序设计实现代码重用的方法是允许定义类之间的关系。而继承是实现该功能的主要手段,其将不同代码中相同的部分提取出来。即抽取不同类的共同属性和行为创建全新的类,实现代码最大限度地重用。
利用类和继承这两个特性,高效地将抽象数据通过类封装起来,即通过类将同一类对象管理起来。因此可以说类是将数据黑盒子化的工具,而继承可以从其它类继承属性,只需要扩展实现或对实现稍作改进,即可支持软件重用。
在经典的Shape(形状)示例中,Circle(圆形)、Square(矩形)和Star(星形)都直接继承自Shape,这种关系通常被称为is-a关系。因为圆是一种形状,矩形也是一种形状,星形也是一种形状,即Circle、Square和Star都是Shape的扩展。当子类继承自父类时,任何父类能做的事情子类都可以做。
3、多态性
当你要求某人画一个形状Shape时,实际上没有人能完成这个任务。因为形状是一个抽象的概念,所以Shape无法提供绘制代码,必须指定一个具体的形状。有了具体形状,就可以为各种具体形状实现各自的绘图代码。
显然,无论画什么形状,其共性是Draw画图方法,每种形状都可以通过函数指针调用各自的绘图代码绘制自己,这就是多态的意义,即多态允许用相同的方法(代码)在运行中,根据对象的类型调用不同的处理函数。
4、组合
组合是指在类中包含一个对象,且该对象是其它类的实例,开发者将责任委托给所包含的对象完成。组合有两种方式:聚合和组合,这些方式表示了对象之间的协作关系。
聚合就是“可聚可散”的意思,被包含的对象如同一个集合。聚合关系是整体与部分的关系,且部分可以离开整体而单独存在。虽然汽车和发动机是整体和部分的关系,但发动机离开汽车仍然可以存在,所以汽车和发动机是聚合关系。
虽然组合关系也是某种形式的整体和部分的聚合,但部分不能离开整体而单独存在,部分对象与整体对象之间具有同生共死的关系。在组合关系中,部分是整体的一部分,且整体可以控制部分的生命周期,即部分的存在依赖于整体。
虽然花瓣不是一种花,但它是花的一部分,因此它们之间存在一种真正的has-a组合关系,不存在父子关系。同样,头部是由眼睛、嘴巴、鼻子和耳朵组合而成的,如果头部不存在,那么这些部件都不能单独存在。
>>> 4.1.3 OO收益
耦合性与内聚性是相辅相成的关系,内聚性描述的是一个模块内部组成部分之间相互联系的紧密程度,而耦合性描述的是一个模块与其它模块之间联系的紧密程度。由此可见,无论使用哪种方法,软件开发的目标是创建符合“高内聚、低耦合”这样的模块。也就是说,每个模块尽可能独立完成某个特定的功能。
如果模块之间做到了低耦合,那么修改一个模块就不需要修改另一个模块。使用模块化最重要的一点是,能够独立修改单个模块,而不需要修改系统的其它模块。一个典型的错误是,使用紧耦合的方式做模块之间的集成,从而使得一个模块的修改会导致其消费者的修改。一个低耦合的模块应该尽可能少地知道与之协作的那些模块的信息,即应该限制两个模块之间不同调用形式的数量,因为除了潜在的性能问题之外,过度的通信可能会导致紧耦合。
内聚性用于评估一个组件(包、模块或配件)中成员的功能相关性,内聚程度高表明各个成员共同完成了一个功能特性或一组功能特性,内聚程度低表明各个成员提供的功能互不相干。如果一个类的方法和属性共同完成了一个功能或一系列紧密相关的功能,这个类就是内聚的。假设有一个这样的类,实现了3种完全不同的功能。如果这3个功能的需求细节发生了变化,这个类也必须跟着改变,从而导致更多的开发和维护成本。因此高内聚就是将相关的行为聚集在一起,而将不相关的行为放在别处。这样做的好处是,如果要修改某个行为,则只在一个地方修改,即可尽快发布。
全部0条评论
快来发表一下你的评论吧 !