“夜半三更哟,盼天明;寒冬腊月哟,盼春风。若要盼得哟,涨工资,岭上……”自从上次老板许诺给小陈涨工资之后,一转眼又过去几个月了,但是涨工资的事一点动静都没有。小陈只好每天哼着这首歌,本身安慰本身,天总会亮的,春天总会来的,而工资也总会涨的。这天,小陈正在哼这首歌,没想到老板又让他去办公室。小陈心中那个高兴啊,心想,盼星星盼月亮,总算盼到了这一天啊。程序员
因而,小陈赶忙来到老板的办公室。但是,当他一进办公室,看到老板那阴云密布的脸就知道状况不妙。果真,老板一见小陈就抱怨起来:数组
“小陈啊,你这个工资程序怎么搞得嘛?你是知道的,咱们公司的工资是按照员工的入职年数来计算的,而且高级员工和通常员工的计算方法不一样。但是过了一年了,每一个人的工资仍是第一次输入的数据,没有变化嘛!还有还有,你这个程序只能找到最高工资的人的序号,只知道序号有什么用啊,我要知道名字,名字,这样我才好直接把他给开了啊……”函数
一听老板这一通抱怨,小陈心凉了半截,心想此次涨工资确定又没戏了。因而有气无力地说:3d
“老板,你别着急,程序就是这样不断改进不断完善的。让我回去按照你的要求修改修改,保证让你满意。”指针
就这样,在老板那里挨了一顿训以后,小陈又带着老板的新要求回来了。小陈简单地分析了一下老板的新要求:要让每一个员工的工资动态计算,而员工又分为高级员工和通常员工两种,每种员工的工资计算方法各不相同。在统计的时候,不只须要给出最高工资者的序号,还要给出姓名的信息。这些新的要求看起来还挺复杂的,小陈正挠头细想解决之道,忽然灵光一闪:这个问题,正好能够用C++中的面向对象思想来解决啊——利用封装机制,能够把员工的序号、入职年份、姓名、工资等信息封装成员工类,这样在统计获得最高工资的员工序号的同时也就获得了对应的姓名;利用继承机制,能够从员工类派生出高级员工类和通常员工类,再配合多态机制,就能够实现对两类员工的工资采起不一样的计算方式了。想到这里,小陈不禁得一拍小腿,心中感叹,面向对象思想在解决复杂问题时果真威力无比啊!巧的是,小陈这段时间恰好学过了C++中用类来体现面向对象思想,因而他决定用类来对这个工资程序进行改写。对象
所谓需求分析,就是搞清楚客户到底要的是一个什么样的软件。不管这个软件是用于飞天登月的大型系统,仍是仅供孩子们玩的游戏程序,需求分析永远都是咱们开发工做的第一步。因此,当小陈接到老板下达的任务后,他作的第一件事不是当即修改程序代码,而是先进行需求的分析,搞清楚老板到底要的是怎样一个工资程序。blog
根据老板的抱怨(在实际的开发实践中,这每每来自前期的用户调查),这个工资程序必须可以输入员工的工资数据,而输入数据又包括直接从数据文件读取和手工输入;完成数据输入之后,这个程序还要对工资数据进行处理,包括统计最高的工资,以及根据员工的姓名对工资进行查询;最后,就是将全部的工资数据输出到文件,以便于下次直接读取。继承
通过这样的简单需求分析,小陈对老板想要的工资程序就比较清楚了。为了让这些需求更加清晰而直观,小陈将其绘制成了UML用例图,老板要的工资程序,不过就是实现了这些用例的工资程序。接口
最佳实践:全世界程序员都在说UML游戏
UML(统一建模语言,Unified Modeling Language),一种描述软件的经常使用方式,它经过为软件创建模型,并经过一系列图(用例图、类图、活动图等)来直观地描述软件的结构和行为,从而让程序员对软件有一个清晰的认识和理解。所以,在具体实现一个软件以前,咱们都使用它来描述咱们即将开发的软件,以期在项目团队中达成对软件的共识。也正由于如此,整个项目团队中的成员,甚至是全世界的程序员,都必须掌握这门建模语言。
图6-13 工资程序的用例图
完成程序的需求分析后,小陈明白了本身要作的是怎样的一个软件,接下来的问题就是怎么作了。按照面向对象思想解决问题的通常顺序,首先就是从问题描述中发现对象。而小陈知道,问题描述中的那些名词实际上就是对象。
按照“寻找对象就是寻找名词”的思路,小陈开始寻找这个问题描述中的名词。首先,遇到的第一个名词是工资系统(SalarySys)。而后就是该系统所管理的员工(Employee),由于级别的不一样,员工又分为高级员工(Officer)和普通员工(Staff),这些就是整个问题中的名词,也就构成了整个问题所涉及的对象。
从问题描述中除了能够找到对象以外,还能够发现对象之间的各类关系:工资系统管理员工对象,它们之间是一对多的关系;同时,高级员工和普通员工同属于员工,这就表示它们应该有着共同的基类,都是从员工类所派生出来的。图6-14描述了整个问题中的对象及对象之间的关系。
图6-14 工资程序中的对象及对象之间的关系
在找到对象以后,就能够进一步分析这些对象所拥有的属性和行为,而后利用面向对象的封装机制将其封装成具体的类。首先,分析这个问题中最基础的员工类Employee。根据老板的要求,为了找到工资最高的员工,咱们必须记录每一个员工的姓名(m_strName);为了根据在职时间(如今时间减去入职时间)动态地计算员工的工资,咱们必须记录员工的入职时间(m_nYear);员工有级别的差异,各个级别的员工工资计算方式不一样,应该有一个属性级别(m_nLevel)来记录。因此这个对象必需的属性就是姓名、入职时间和级别。
分析了员工类Employee的属性,那么它又该拥有什么样的行为呢?类的行为都是用来完成需求分析中的用例的,因此,Employee类的行为跟它要完成的用例密切相关。为了完成“计算最大值”用例,它应该有一个计算工资的行为(GetSalary()),能够根据员工的在职时间动态地计算员工的工资。可是,Employee类做为具体的员工类Officer和Staff的基类,并不知道工资的具体计算方法,因此这个行为只是一个接口而已,须要留待它的派生类来具体实现,因此在Employee中这个函数应该是一个纯虚函数;而要计算工资,它又必须知道员工的在职时间,因此它还必须有一个得到在职时间的行为(GetWorkTime());同时,为了完成“查询工资”这个用例,程序须要知道员工的姓名,因此员工类应该提供一个得到名字的行为(GetName());最后,为了完成“输出数据到文件”的用例,Employee类还必须提供得到员工级别(GetLevel())和入职年份(GetYear())的行为,从而能够获取员工的信息并将其输出。
通过这样的分析,小陈得出了员工类Employee应该具有的属性和行为。为了记录本身的分析结果,让结果一目了然,小陈将分析结果画成了UML类图:
图6-15 Employee类的属性和行为
具体的员工类Officer和Staff是Employee的派生类,在Employee类的基础上,这两个具体的员工类并无额外的须要描述的内容,因此它们不须要新添加属性,只须要从基类继承已有的属性便可。而至于行为,具体的员工类须要负责具体的工资计算和返回不一样的员工级别,因此它们须要实现基类中的GetSalary()和GetLevel()这两个虚函数。通过这样的分析,Officer和Staff类应该具有的属性和方法就很清楚了。小陈将它们用以下的UML类图来表示:
图6-16 Officer和Staff类的属性和行为。
按照一样的方法,小陈接着分析用于管理这些员工对象的SalarySys类。为了保存和管理多个Employee对象,SalarySys类必须有一个数组来保存这些对象,而为了应用面向对象的多态机制来动态地计算员工工资,数组中保存的不该该是这些对象自己,而应该是指向这些对象的指针;同时,数组只是表示了SalarySys所可以保存的最多的对象指针,可是并非数组中的每一个指针都是有效的,具体保存了多少个指针还不清楚,咱们还必须用一个属性来表示当前有效的指针的个数(m_nCount);另外,SalarySys须要从文件读取数据,最后还须要将数据写入文件,因此它还须要一个记录数据文件名的属性(m_strFileName)。
在SalarySys类的行为上,小陈仍是一样从它要完成的用例来分析。根据他以前对这个程序进行的简单需求分析,SalarySys首先须要完成“输入数据”这个用例,而这个用例又包含了“从文件读取”和“手工输入”这两个用例,这就要求SalarySys类应该具备从文件读取数据(Read())和让用户手工输入(Input())的行为;完成“输入数据”以后,就是“处理数据”,它也一样包括了“计算最大值”和“查询工资”两个用例,这就要求SalarySys类具备查找全部工资数据中的最大值(GetMax())和根据用户输入的姓名查询相应工资信息(Find())的行为;最后,就是“输出数据”这个用例,它要求SalarySys具备将全部工资数据保存到数据文件(Write())的行为。
分析完成以后,小陈一样将分析结果绘制成了UML类图:
图6-17 SalarySys类的属性和行为