2017编程提高第4节课——面向对象设计(4)
LanyuanXiaoyao's Blog ヽ(✿゚▽゚)ノ

2017编程提高第4节课——面向对象设计(4)


先来学几个简单的单词

  • Salary: 薪水
  • Employee: 员工
  • Sales Receipt : 销售凭条
  • Commission:  佣金
  • Classification: 分类
  • Payment:  支付
  • Affiliation : 从属关系

需求

  1. 该系统由一个公司数据库以及和雇员相关的数据(例如工作时间卡)组成,系统需要准时地按照规则给员工支付薪水,同时,必须从薪水中扣除各种扣款(会费)
  2. 有些雇员是钟点工, 会按照他们雇员记录中每小时的报酬字段的值对他们进行支付,他们每天提交工作时间卡,其中记录了日期以及工作小时数,如果每天工作超过8小时,按1.5倍进行支付。 每周五对他们进行支付。
  3. 有些雇员完全以月薪进行支付,每个月的最后一个工作日对他们进行支付,在他们的雇员记录中有个月薪字段
  4. 同时,对于一些带薪的雇员,会根据他们的销售情况,支付给他们一定数量的佣金,他们会提交销售凭条,其中记录了销售的日期数量。在他们的雇员记录中有一个酬金报酬字段。 每隔一周的周五对他们进行支付。
  5. 雇员可以选择支付方式,可以把支票邮寄到他们指定的邮政地址,也可以保存在财务那里随时支取,或者要求直接存入他们指定的银行账户
  6. 一些雇员会加入协会,在他们的雇员记录中有一个每周应付款项字段,这些应付款需要从他们的薪水中扣除。 协会有时会针对单个会员征收服务费用。 协会每周会提交这些服务费用,服务费用从相应雇员的薪水总额中扣除。
  7. 薪水支付程序每个工作日运行一次, 并在当天对相应的雇员进行支付,系统会被告知雇员的支付日期,这样它会计算从雇员上次支付日期到规定的支付日期间应付的数额。

如果有人身兼多个角色?比如一个月薪雇员也是钟点工?如果有人只是转换了角色?曾经是月薪工变成了钟点工?

开始设计

TimeCard 时间卡——记录钟点工干了多少小时
SalesReceipt 销售凭条——记录销售员工的销售情况

很明显我们需要一个Employee

对 Employee 和 TimeCard 之间的关系使用的是组合,毕竟时间卡是不能脱离具体员工单独存在的

还是把职责分离一下吧

支付方式

雇员可以选择支付方式,可以把支票邮寄到他们指定的邮政地址,也可以保存在财务那里随时支取,或者要求直接存入他们指定的银行账户

但是怎么把这三个支付方式和刚才设计的类Employee进行关联?
毫无疑问,需要抽象

PaymentMethod出场

纳入到Employee中来

文档中没有描述的需求

  • 可以把钟点工改成带薪雇员! 可以把带薪雇员改成销售…
  • 反思:一些雇员按小时工作,一些雇员以月薪工作,一些雇员会支付一定数量的酬金。
  • 思路: 可以在Employee中加一个employeeType的字段
    1. 表示 钟点工
    2. 表示带薪雇员
    3. 表示销售

还得抽象: PaymentClassification

Employee不再使用不同的子类来区分角色,让角色也成为一个接口,这里是使用工资支付方式来确定他们的身份关系,也就相当于给了他们一个角色,使用小时支付方式的就是钟点工,使用月薪支付方式的就是月薪员工

关于抽象类和接口该如何选择的问题:
公共行为多,需要一个父类的时候→抽象类
公共行为少→接口

在哪里计算薪水

  • PaymentClassification 是个不错的地方, 因为它的子类对应于不同的员工类型
    • 对于一个给定的日期,如果这一天应该支付薪水,则进行薪水的计算

这个实现的逻辑存在的问题:如果发薪日改变了?比如我们原本是月底支付月薪,现在变成月中支付?
实际上这违反了单一职责原则和开闭原则,因为我们不能动态地增加逻辑而且发薪日的判断也不应该在薪水计算当中,薪水计算逻辑和日期判断应该是一个正交关系

抽象再次上场: PaymentSchedule

  • WeeklySchedule : 每周五支付
  • BiWeeklySchedule : 每隔一周支付
  • MonthlySchedule : 每月的最后一天支付

Employee的各个接口都是独立变化,互不影响——策略模式 + 组合

薪水计算和payment schedule隔离开

抽象完成之后,在Employee中对各个逻辑的操作就是一个笼统的描述,具体的实现留给了接口*

计算薪水的细节问题

  • 钟点工: sum (每个时间卡 * 每小时报酬)
    • 对于钟点工,只计算过去一周的时间卡 如何表示过去一周这个时间概念?
  • 销售: 基础薪水 + sum ( 每个销售凭条的销售额* rate )
    • 对于销售, 只计算过去两周的销售凭条
  • 普通员工: 固定的薪水
  • 薪水支付程序每天都会运行, 甚至一天运行多次,会不会产生多次发薪的错误? 首先这个系统是要允许重复运行的

抽象再次上场: Paycheck

  • 需要把成功运行的支付记录保存下来
    • 可以检查是否重复运行
  • 需要有一个时间段(Period)的概念
    • 能表示过去一周,过去两周

  • 时间段
    • payPeriodStart
    • payPeriodEnd
  • 应付款
    • grossPay
  • 扣除款
    • deductions
  • 实发款
    • netPay

实际上这个Paycheck是一个参数集合

Show me the code

问题:

  • e.isPayDay() 是怎么实现的?
  • e.getPayPeridStartDate() 是怎么实现的?

每隔一周的周五支付

如何解决支付方式的转换,比如有人使用双周支付一周→单周支付三周→双周支付

别忘了这个需求

  • 一些雇员会加入协会,在他们的雇员记录中有一个每周应付款项字段,这些应付款需要从他们的薪水中扣除。
  • 协会有时会针对单个会员征收服务费用。 协会每周会提交这些服务费用,服务费用从相应雇员薪水总额中扣除。

会员费用

Null Object 模式:NonAffiliation

即使是不扣除任何款项也有一个对应的类,这样可以避免Employee使用时候需要加入NULL校验

如何创建员工对象?

模板方法

代码

作业

自己去编码完成薪水支付案例的核心接口及其实现:

  • Employee
  • PaymentClassification
  • PaymentSchedule
  • PaymentMethod
  • PayCheck
  • Affiliation
  • SalesReceipt
  • TimeCard


评论