6.4  工资程序成长记:类与对象

“夜半三更哟,盼天明;寒冬腊月哟,盼春风。若要盼得哟,涨工资,岭上……”自从上次老板许诺给小陈涨工资以后,一转眼又过去几个月了,可是涨工资的事一点动静都没有。小陈只好天天哼着这首歌,自己安慰自己,天总会亮的,春天总会来的,而工资也总会涨的。这天,小陈正在哼这首歌,没想到老板又让他去办公室。小陈心中那个高兴啊,心想,盼星星盼月亮,总算盼到了这一天啊。

于是,小陈赶紧来到老板的办公室。可是,当他一进办公室,看到老板那阴云密布的脸就知道情况不妙。果然,老板一见小陈就抱怨起来:

“小陈啊,你这个工资程序怎么搞得嘛?你是知道的,我们公司的工资是按照员工的入职年数来计算的,并且高级员工和一般员工的计算方法不同。可是过了一年了,每个人的工资还是第一次输入的数据,没有变化嘛!还有还有,你这个程序只能找到最高工资的人的序号,只知道序号有什么用啊,我要知道名字,名字,这样我才好直接把他给开了啊……”

一听老板这一通抱怨,小陈心凉了半截,心想这次涨工资肯定又没戏了。于是有气无力地说:

“老板,你别着急,程序就是这样不断改进不断完善的。让我回去按照你的要求修改修改,保证让你满意。”

就这样,在老板那里挨了一顿训之后,小陈又带着老板的新要求回来了。小陈简单地分析了一下老板的新要求:要让每个员工的工资动态计算,而员工又分为高级员工和一般员工两种,每种员工的工资计算方法各不相同。在统计的时候,不仅需要给出最高工资者的序号,还要给出姓名的信息。这些新的要求看起来还挺复杂的,小陈正挠头细想解决之道,突然灵光一闪:这个问题,正好可以用C++中的面向对象思想来解决啊——利用封装机制,可以把员工的序号、入职年份、姓名、工资等信息封装成员工类,这样在统计得到最高工资的员工序号的同时也就得到了对应的姓名;利用继承机制,可以从员工类派生出高级员工类和一般员工类,再配合多态机制,就可以实现对两类员工的工资采取不同的计算方式了。想到这里,小陈不由得一拍小腿,心中感叹,面向对象思想在解决复杂问题时果然威力无比啊!巧的是,小陈这段时间刚好学过了C++中用类来体现面向对象思想,于是他决定用类来对这个工资程序进行改写。

6.4.1 需求分析:老板要的是这样一个工资程序

所谓需求分析,就是搞清楚客户到底要的是一个什么样的软件。无论这个软件是用于飞天登月的大型系统,还是仅供孩子们玩的游戏程序,需求分析永远都是我们开发工作的第一步。所以,当小陈接到老板下达的任务后,他做的第一件事不是立即修改程序代码,而是先进行需求的分析,搞清楚老板到底要的是怎样一个工资程序。

根据老板的抱怨(在实际的开发实践中,这往往来自前期的用户调查),这个工资程序必须能够输入员工的工资数据,而输入数据又包括直接从数据文件读取和手工输入;完成数据输入以后,这个程序还要对工资数据进行处理,包括统计最高的工资,以及根据员工的姓名对工资进行查询;最后,就是将所有的工资数据输出到文件,以便于下次直接读取。

经过这样的简单需求分析,小陈对老板想要的工资程序就比较清楚了。为了让这些需求更加清晰而直观,小陈将其绘制成了UML用例图,老板要的工资程序,不过就是实现了这些用例的工资程序。

最佳实践:全世界程序员都在说UML

UML(统一建模语言,Unified Modeling Language),一种描述软件的常用方式,它通过为软件建立模型,并通过一系列图(用例图、类图、活动图等)来直观地描述软件的结构和行为,从而让程序员对软件有一个清晰的认识和理解。因此,在具体实现一个软件之前,我们都使用它来描述我们即将开发的软件,以期在项目团队中达成对软件的共识。也正因为如此,整个项目团队中的成员,甚至是全世界的程序员,都必须掌握这门建模语言。

图6-13 工资程序的用例图

6.4.2  从问题描述中发现对象

完成程序的需求分析后,小陈明白了自己要做的是怎样的一个软件,接下来的问题就是怎么做了。按照面向对象思想解决问题的一般顺序,首先就是从问题描述中发现对象。而小陈知道,问题描述中的那些名词实际上就是对象。

按照“寻找对象就是寻找名词”的思路,小陈开始寻找这个问题描述中的名词。首先,遇到的第一个名词是工资系统(SalarySys)。然后就是该系统所管理的员工(Employee),因为级别的不同,员工又分为高级员工(Officer)和普通员工(Staff),这些就是整个问题中的名词,也就构成了整个问题所涉及的对象。

从问题描述中除了可以找到对象之外,还可以发现对象之间的各种关系:工资系统管理员工对象,它们之间是一对多的关系;同时,高级员工和普通员工同属于员工,这就表示它们应该有着共同的基类,都是从员工类所派生出来的。图6-14描述了整个问题中的对象及对象之间的关系。

图6-14  工资程序中的对象及对象之间的关系

6.4.3  分析对象的属性和行为

在找到对象之后,就可以进一步分析这些对象所拥有的属性和行为,然后利用面向对象的封装机制将其封装成具体的类。首先,分析这个问题中最基础的员工类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类的属性和行为

你好,C++(38)从问题描述中发现对象的属性和行为 6.4 工资程序成长记:类与对象(上)的更多相关文章

  1. 你好,C++(23) 4.4.2 工资程序成长记:用数组处理批量数据,用循环结构执行重复动作

    4.4  从语句到程序 了解了各种表达式和语句之后,就相当于掌握了写作文要用到的词语和句子,但是,仅有词语和句子是无法构成一篇有意义的文章的.要完成一篇文章,先需要确定这篇文章的结构,是先分述再总述, ...

  2. 你好,C++(30)“大事化小,小事化了”5.4.3 工资程序成长记:函数

    5.4.3 工资程序成长记:函数 自从上次小陈“程序员”的工资程序得到老板的夸奖,口头许诺给他涨工资以后,老板再也没有找过他,涨工资的事自然也就没有下文了.这天,老板又突然召他去办公室.这下可把小陈高 ...

  3. 【Azure API 管理】从微信小程序访问APIM出现200的空响应的问题中发现CORS的属性[terminate-unmatched-request]功能

    问题描述 使用微信小程序调用APIM(API Management)中的接口,发现POST和PUT请求被拦截,返回的状态码为200,但是无消息内容. 在小程序中的调用JS代码如: 通过浏览器测试得到的 ...

  4. CSS和SVG中的剪切——clip-path属性和<clipPath>元素

    剪切是什么 剪切是一个图形化操作,你可以部分或者完全隐藏一个元素.被剪切的元素可以是一个容器也可以是一个图像元素.元素的哪些部分显示或隐藏是由剪切的路径来决定的. 剪切路径定义了一个区域,在这个区域内 ...

  5. 【转】CSS和SVG中的剪切——clip-path属性和<clipPath>元素

    本文由大漠根据SaraSoueidan的<Clipping in CSS and SVG – The clip-path Property and <clipPath> Elemen ...

  6. 浅谈JavaScript和DOM中的类数组对象

    JavaScript是一门弱类型语言,它的数据类型分为两大类:简单数据类型(5种:Undefined.Null.Boolean.Number.String)和复杂数据类型(1种:Object).Obj ...

  7. 使用java中的反射获得object对象的属性值

    知识点:使用java中的反射获得object对象的属性值 一:场景 这两天开发代码时,调用别人的后台接口,返回值为Object对象(json形式的),我想获得object中指定的属性值,没有对应的ge ...

  8. C#类中的字段、属性和方法

    C#类中的字段.属性和方法 刚开始学C#,对于类中的字段.属性和方法很难分清,写下这份笔记,帮助理解 字段:与类相关的变量 声明方法与声明变量类似,可在前面添加访问修饰符.static关键字等: 属性 ...

  9. RDP 协议组件 X.224 在协议流中发现一个错误并且中断了客户端连接

    如果你的服务器有如下错误: “RDP 协议组件 X.224 在协议流中发现一个错误并且中断了客户端连接.” 可能的有2种: 1:你试试能否能继续远程登陆,有可能你的远程登陆组件出现问题. 2:有人攻击 ...

随机推荐

  1. Code (组合数)

    Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 7184   Accepted: 3353 Description Trans ...

  2. (转载)PHP 动态生成表格

    (转载)http://hi.baidu.com/shawns/item/c7d51f351c6a0482b711dba6 提要:PHP能够高效地生成HTML代码,其中,动态生成表格是实际应用中经常碰到 ...

  3. Android Service命令

    service可给Android 服务传消息,具体用法如下: Usage: service [-h|-?]        service list        service check SERVI ...

  4. RAII(Resource Acquisition Is Initialization)资源获得式初始化

    当在编写代码中用到异常,非常重要的一点是:“如果异常发生,程序占用的资源都被正确地清理了吗?” 大多数情况下不用担心,但是在构造函数里有一个特殊的问题:如果一个对象的构造函数在执行过程中抛出异常,那么 ...

  5. Spring二 Bean详解

    Bean详解 Spring框架的本质其实是:通过XML配置来驱动Java代码,这样就可以把原本由java代码管理的耦合关系,提取到XML配置文件中管理.这样就实现了系统中各组件的解耦,有利于后期的升级 ...

  6. mysql数据类型介绍

    一.int.bigint.smallint 和 tinyint的区别详细介绍 bigint 从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854 ...

  7. 微信小程序开发手册

    目录: 数据绑定 条件渲染 列表渲染 API FAQ: <block wx:for> 和 <view wx:for>的区别 wx:if 什么情况下判断为假 坑列表: 微信版本6 ...

  8. iphone匹配邮箱的正则表达式

    NSString *str = [NSString stringWithString:@"\\b([a-zA-Z0-9%_.+\\-]+)@([a-zA-Z0-9.\\-]+?\\.[a-z ...

  9. Java注解的简单了解

    部分信息来自<Thinking In Java> 注解也成为元数据.什么是元数据?就是“关于数据的数据” 注解为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便 ...

  10. [PWA] 0. Introduce to Offline First

    Why offline first? Imagin you are visiting a website, it is fine if wifi connection is good. It migh ...