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


    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类的属性和行为

     

  • 相关阅读:
    leetcode 131. Palindrome Partitioning
    leetcode 526. Beautiful Arrangement
    poj 1852 Ants
    leetcode 1219. Path with Maximum Gold
    leetcode 66. Plus One
    leetcode 43. Multiply Strings
    pytorch中torch.narrow()函数
    pytorch中的torch.repeat()函数与numpy.tile()
    leetcode 1051. Height Checker
    leetcode 561. Array Partition I
  • 原文地址:https://www.cnblogs.com/nihaoCPP/p/4459922.html
Copyright © 2020-2023  润新知