目前在软件开发领域有两种主流的开发方法:结构化开发和面向对象开发。结构化开发是一种比较传统的开发方法,早期的高级编程语言,如Basic、C. Fortran和Pascal等,都是支持结构化开发的编程语言。随着软件开发技术的逐步发展,为了进一步提高软件的可重用性、可扩展性和可维护性,面向对象的编程语言及面向对象设计理论应运而生,JAVA语言就是一种纯面向对象的编程语言。
一般说来,软件开发都会经历以下生命周期:
为了提高软件开发效率,降低软件开发成本,一个优良的软件系统应该具备以下特点:
结构化开发方法主要是按照功能来划分软件结构的,它把软件系统的功能看作是根据给定的输入数据,进行相应的运算,然后输出结果,如下图所示。
进行结构化设计时,首先考虑整个软件系统的功能,然后按照模块划分的一些基本原则(比如内聚性和松耦合)等,对功能进行分解,把整个软件系统划分成多个模块,每个模块实现特定的子功能。为了提高软件的内聚性,在模块中还会把功能分解到更小的子模块中。在完成所有的模块设计后,把这些模块拼装起来,就构成了整个软件系统。软件系统可看作是多个子系统的集合,每个子系统都是具有输入/输出功能的模块,如下图所示。
结构化设计属于自顶向下的设计,在设计阶段就不得不考虑如何实现系统的功能,因为分解功能的过程其实就是实现功能的过程。结构化设计的局限性在于不能灵活地适应用户不断变化的需求。当用户需求发生变化,比如要求修改现有软件功能的实现方式或者要求追加新的功能时,就需要自顶向下地修改模块的结构,有时候甚至整个软件系统的设计被完全推翻。
在进行结构化编程时,程序的主体是方法,方法是最小的功能模块,每个方法都是具有输入/输出功能的子系统,方法的输入数据来自于方法参数、全局变量和常量,方法的输出数据包括方法返回值,以及指针类型的方法参数。一组相关的方法组合成大的功能模块。
面向对象的开发方法把软件系统看成是各种对象的集合,对象就是最小的子系统,一组相关的对象能够组合成更复杂的子系统。面向对象的开发方法具有以下优点:
广义地讲,面向对象编程是结构化编程的一种改进实现方式。传统的面向过程的结构化编程的最小子系统是功能模块,而面向对象编程的最小子系统是对象。
对象模型
在面向对象的分析和设计阶段,致力于建立模拟问题领域的对象模型。建立对象模型既包括自底向上的抽象过程,也包括自顶向下的分解过程。
(1)自底向上的抽象。
建立对象模型的第一步是从问题领域的陈述入手。分析需求的过程与对象模型的形成过程一致,开发人员与用户的交谈是从用户熟悉的问题领域中的事物(具体实例)开始的,这就使用户与开发人员之间有了共同语言,使得开发人员能彻底搞清用户需求,然后再建立正确的对象模型。开发人员需要进行以下自底向上的抽象思维:
(2)自顶向下的分解。
在建立对象模型的过程中,也包括自顶向下的分解。例如对于计算机系统,首先识别出主机对象、显示器对象、键盘对象和打印机对象等。接着对这些对象再进一步分解,例如主机对象由处理器对象、内存对象、硬盘对象和主板对象等组成。系统的进一步分解因有具体的对象为依据,所以分解过程比较明确,而且也相对容易。所以面向对象建模也具有自顶向下开发方法的优点,既能有效地控制系统的复杂性,又同时避免了结构化开发方法中功能分解的困难和不确定性。
UML:可视化建模语言
面向对象的分析与设计方法,在20世纪80年代末至90年代中发展到一个高潮。但是,诸多流派在思想和术语上有很多不同的提法,对术语和概念的运用也各不相同,统一是继续发展的必然趋势。需要用一种统一的符号来描述在软件分析和设计阶段勾画出来的对象模型,统一.建模语言(Unified Modeling Language, UML)应运而生。
UML是一种定义良好、易于表达、功能强大且普遍适用的可视化建模语言。它吸取了诸多流派的优点,而且有进一步的发展,最终成为大众所共同接受的标准建模语言。
在面向对象的软件开发过程中,开发者的主要任务就是先建立模拟问题领域的对象模型,然后通过程序代码来实现对象模型。到底如何建立对象模型,如何用程序代码实现对象模型,并且能保证软件系统的可重用、可扩展和可维护性呢?
问题领域、对象、属性、状态、行为、方法、实现
问题领域是指软件系统所模拟的真实世界中的系统。随着计算机技术的发展和普及,软件系统渗透到社会的各个方面,几乎可用来模拟任意一种问题领域,如学校、医院、商场、银行、电影摄制组和太阳系等。
对象是对问题领域中事物的抽象。对象具有以下特性:
(1)万物皆为对象。问题领域中的实体和概念都可以抽象为对象。例如在学校领域,对象包括学生、成绩单、教师、课程和教室等:在银行领域,对象包括银行账户、出纳员、支票、汇率、现金和验钞机等;在商场领域,对象包括客户、商品、订单、发票、仓库和营业员等:在电影摄制组领域,对象包括演员、导演、电影、道具和化妆师等;在太阳系领域,对象包括太阳、月亮、地球、火星和木星等;在用Java语言创建的图形用户界面中,窗口、滚动面板、按钮、列表、菜单和文本框等也都是对象。
(2)每个对象都是唯一-的。对象的唯一性来自于真实世界中事物的唯一性。世界上不存在两片一模一样的叶子,因此在软件系统中用来模拟每片叶子的对象也具有唯一性。例如学校领域的学生小张、学生小王、小张的成绩单和小王的成绩单,这些都是唯一的对象。在Java虚拟机提供的运行时环境中,保证每个对象的唯一性的手段是为它在内存中分配唯一的地址。
(3)对象具有属性和行为。例如小张,性别女,年龄15,身高1.6米,体重40kg, 能够学习、唱歌和打羽毛球。小张的属性包括:姓名、性别、年龄、身高和体重。小张的行为包括:学习、唱歌和打羽毛球。例如一部手机:品牌名称是诺基亚,价格是2000元,银白色,能够拍照、打电话和收发短信等。这只手机的属性包括:品牌类型type、 价格price 和颜色color,行为包括拍照takePhoto()、 打电话call)、 收短信sendMessage(和发短信receiveMessage()。
同一个类的所有实例具有相同属性,表明它们的属性的含义相同,但是它们的状态不一定相同,也就是属性取值不一定相同。例如演员小红、小白和小黄,都有姓名、性别、年龄、身高和体重这些属性,但是他们的属性取值不同。
同一个类的所有实例包括类本身的所有实例,以及其子类的所有实例。类的所有实例具有相同行为,意味着它们具有一些相同的功能。类本身的所有实例按同样的方式实现相同的功能,而子类与父类之间,以及子类之间的实例则可能采用不同的方式来实现相同的功能。
类是一组具有相同属性和行为的对象的抽象。类及类的关系构成了对象模型的主要内容。如下图所示,对象模型用来模拟问题领域,Java 程序实现对象模型,Java程序运行在Java虚拟机提供的运行时环境中,Java虚拟机运行在计算机机器上。
计算机受其存储单元的限制,只能表示和操作一些基本的数据类型,比如整数、字符和浮点数。对象模型中的类可以看作是开发人员自定义的数据类型,Java 虛拟机的运行时环境封装了把自定义的数据类型映射到计算机的内置数据类型的过程,使得开发人员不必受到计算机的内置数据类型的限制,对任意一种问题领域,都可以方便地根据识别对象、再进行分类(创建任意的数据类型)的思路来建立对象模型。
消息、服务
软件系统的复杂功能是由各种对象向电视机对象发送一个“开机”消息。电视机对象接受到这个“开机”消息,执行相应的开机操作。此外,遥控器还能向电视机发送其他消息,例如选择频道、调节音量、播放VCD和关机等。
每个对象都具有特定的功能,相对于其他对象而言,它的功能就是为其他对象提供的服务。例如电视机具有的功能包括:开机、关机、选择频道、调节音量和播放VCD等。遥控器为了获得电视机的服务,需要向电视机提出获得特定服务的请求,提出请求的过程称为发送消息。对象提供的服务是由对象的方法来实现的,因此发送消息实际上也就是调用一个对象的方法。
接口
既然每个对象是服务提供者,那么如何对外提供服务呢?对象通过接口对外提供服务。例如电视机的红外线接收器就是为遥控器提供的接口。再比如在日常生活中经常接触的电源插口,如果把整个供电系统看作-一个对象,那么它提供的主要服务就是供电。如何提供这一服务呢? 很简单,只要在住宅里布置好线路,提供一些电源插口,各种电器就能从电源插口中获得电源了。电源插口就是供电系统为各种电器提供的接口。此外,鼠标上的按钮、键盘上的按钮、洗衣机上的按钮、电灯的开关都是为用户提供的接口。
在现实世界中,接口也是实体,比如电源插口、洗衣机上的按钮和电灯的开关。而在面向对象的范畴中,接口是一个抽象的概念,是指系统对外提供的所有服务。系统的接口描述系统能够提供哪些服务,但是不包含服务的实现细节。这里的系统既可以指整个软件系统,也可以指一个子系统。对象是最小的子系统,每个对象都是服务提供者,因此每个对象都有接口。
在Java语言中,接口有两种意思:
封装
封装是指隐藏对象的属性和实现细节,仅仅对外公开接口。封装能为软件系统带来以下优点:
(1)便于使用者正确、方便地理解和使用系统,防止使用者错误修改系统的属性。还是以供电系统为例,过去房屋墙壁的.上方都是电线,现在的房屋里电线都“不见”了,在墙壁上只露出了一些电源插口。为什么要把电线藏起来呢?理由很简单,暴露在外面的电线不安全、也不美观。
再比如电视机系统,尽管它本身的实现很复杂,但用户使用起来却非常简单,只要通过遥控器上的几个按钮就能享受电视机提供的服务。电视机的实现细节被藏在它的大壳子里,没有必要向用户公开。
(2)有助于建立各个系统之间的松耦合关系,提高系统的独立性。当某一个系统的实现发生变化,只要它的接口不变,就不会影响到其他的系统。
(3)提高软件的可重用性,每个系统都是-一个相对独立的整体,可以在多种环境中得到重用。例如干电池就是- -个可重用的独立系统,在相机、手电筒、电动剃须刀和玩具赛车中都能发挥作用。
(4)降低了构建大型系统的风险,即使整个系统不成功,个别的独立子系统有可能依然是有价值的。例如相机损坏了,它的干电池依然有用,可以安装到手电筒中。
一个设计良好的系统会封装所有的实现细节,把它的接口与实现清晰地隔离开来,系统之间只通过接口进行通信。面向对象的编程语言主要是通过访问控制机制来进行封装的,这种机制能控制对象的属性和方法的可访问性。在Java语言中提供了4种访问控制级别:
继承
在父类和子类之间同时存在着继承和扩展关系。子类继承了父类的属性和方法,同时,子类中还可以扩展出新的属性和方法,并且还可以覆盖父类中方法的实现方式。覆盖也是专用术语,是指在子类中重新实现父类中的方法。
从每个对象都是服务提供者的角度来理解,子类会提供和父类相同的服务,此外子类还可以提供父类所没有的服务,或者覆盖父类中服务的实现方式。例如手机,早期生产的手机具有打电话的功能,但不具有拍照功能,现在的一些新款手机继承了打电话的功能,而且增加了拍照功能。
继承与扩展同时提高了系统的可重用性和可扩展性。例如,手机和计算机之所以能迅猛地更新换代,具备越来越多的功能,就是因为当厂商在生产新型号的手机和计算机时,他们不必从头生产,而是在原有手机和计算机的基础上进行升级。
继承与扩展导致面向对象的软件开发领域中架构类软件系统的发展。从头构建一个复杂软件系统的工作量巨大,为了提高开发效率,有一些组织开发了一些通用的软件架构。有了这些软件架构,新的软件系统就不必从头开发,只需要在这些通用软件架构的基础上进行扩展。
目前在Java领域比较流行的架构软件包括:
1997年,对象管理组织(Object Management Group, OMG)发布了统一建模语言(Unified Modeling Language, UML)。UML的目标之一就是为开发团队提供标准、通用的面向对象设计语言。UML提供了一套IT专业人员期待多年的统一的标准 建模符号。通过使用UML,这些人员能够阅读和交流系统架构图和设计规划图,就像建筑人多年来使用建筑设计图一样。
UML采用一些标准图形元素来直观地表示对象模型,所以它是一种可视化的面向对象的建模语言。UML主要包含以下一些框图:
在以上框图中,其中用例图、类框图、组件图和部署图等4个图形,构成了系统的静态模型;而状态转换图、时序图和协作图则构成了系统的动态模型。因此,UML的主要框图也可以归纳为静态模型和动态模型两大类。
本篇结合实际例子,探讨了如何运用面向对象思维来构建可维护、可重用和可扩展的软件系统。面向对象的核心思想和概念包括:抽象、封装、接口、多态和继承,灵活运用这些理论武器,就会使得软件系统像用积木搭起来的系统一 样,可以方便地进行组装、拆卸和重用。
喜欢文章请多多点赞评论转发,关注笔者,后续会再带来更丰富的学习内容更新,你们的支持就是笔者最大的动力!!!