员工薪水支付系统介绍

很早以前学习了敏捷软件开发:原则、模式与实践中的薪水支付案例,有些代码设计思想觉得特别好的,特来分享下。
本系统实现了一个计算员工薪水,并发放薪水的功能,该系统必须为不同类型的员工支付正确的薪水,员工类型和结构如下:

  • 有些员工是钟点工。会按照他们工作记录中每小时报酬字段的值对他们进行支付。他们每天会提交工作时间卡,其中记录了日期以及工作小时数。如果每天工作超过8小时,那么超过的部分会按照正常报酬的1.5倍进行支付。每周五对他们进行支付。
  • 有些员工完全以月薪进行支付。每个月的最后一个工作日对他们进行支付。在他们的雇员记录中有一个月薪字段。
  • 还有些销售员工,除了基本工资外,会根据他们的销售情况,支付他们一定数量的酬金。他们会提交销售凭条,其中记录了销售日期和数量。在他们的工作记录中有一个酬金字段,每隔一周的周五对他们进行支付。
    员工还可以自由选择发放薪水方式,可以选择银行卡发放、把支票保存在出纳人员那里随时支取或者通过邮寄方式发放。
    一些员工还会加入协会。每周需要扣除相应会费,协会有时也会针对单个协会成员征收服务费用。
    刚学Java的时候,学习视频里的讲师有提到写代码时要一个类一个方法只做一件事,遇到会变化的内容时要知道抽象出来,我觉得在这个系统里得到了充分的体现。
    有3种类型的员工,一般我们可以这么设计:


设计一个父类员工类,然后分别设计小时工、固定薪资员工、销售员工去继承父类员工类,但是这里有个问题,假如哪天有个需求说要把固定薪资员工转为小时工,或者把小时工转为销售员工时,我们该怎么办,很明显子类间是不能转化的。
解决方法是进行抽象,一些员工按小时支付,一些员工按固定薪资支付,一些员工按销售业绩支付。每种类型的员工都需要被支付,只是支付的策略不同,所以我们可以把支付的策略抽象出来。这就有了支付策略接口:

public interface PaymentClassification {//抽象出支付策略类double calculatePay(Paycheck payCheck);
}

员工类不用理会员工的支付形式,把支付形式交由支付策略接口去解决,由此的到三个实现类:
按工作小时支付:

/** 小时工即按小时支付,把按小时支付计算单独拿出来实现*/
public class HourlyClassification implements PaymentClassification {private double hourlyRate;//每小时工钱private List<TimeCard> timeCards=new ArrayList<TimeCard>();//记录员工的工作时间卡public HourlyClassification(double hourlyRate) {this.hourlyRate = hourlyRate;}public double calculatePay(Paycheck payCheck) {// TODO Auto-generated method stubdouble totalPay=0;for(TimeCard card:timeCards) {if(DateUtil.between(card.getDate(), payCheck.getPayPeriodStart(), payCheck.getPayPeriodend()))totalPay+=calculatePayForTimeCard(card);}return totalPay;}public void addTimeCard(TimeCard timeCard) {this.timeCards.add(timeCard);}public double calculatePayForTimeCard(TimeCard timeCard) {double hours=timeCard.getHours();if(hours>8) {return (8*hourlyRate+(hours-8)*hourlyRate*1.5);}else {return hours*hourlyRate;}}@Overridepublic String toString() {return "HourlyClassification [hourlyRate=" + hourlyRate + ", timeCards=" + timeCards + "]";}}

按固定薪资支付:

/** 固定薪资支付*/
public class SalariedClassification implements PaymentClassification {private double salary;public SalariedClassification(double salary) {this.salary=salary;} public double calculatePay(Paycheck payCheck) {// TODO Auto-generated method stubreturn salary;}@Overridepublic String toString() {return "SalariedClassification [salary=" + salary + "]";}}

按销售情况支付:

/** 销售员工薪资计算*/
public class CommissionedClassification implements PaymentClassification {private double rate;//每笔销售单提成private double salary;//底薪private List<SalesReceipt> salesReceipt=new ArrayList<SalesReceipt>();public CommissionedClassification(double rate, double salary) {super();this.rate = rate;this.salary = salary;}public void addSalesReceipt(SalesReceipt sr) {this.salesReceipt.add(sr);}public double calculatePay(Paycheck payCheck) {// TODO Auto-generated method stubdouble commission=0;for(SalesReceipt sr:salesReceipt) {if(DateUtil.between(sr.getDate(), payCheck.getPayPeriodStart(), payCheck.getPayPeriodend()))commission+=sr.getAmount()*rate;}return commission+salary;}@Overridepublic String toString() {return "CommissionedClassification [rate=" + rate + ", salary=" + salary + ", salesReceipt=" + salesReceipt+ "]";}}

支付策略已经抽象了,还有发放薪水的方式,继续抽象,把发放薪水的方式交由以下接口处理:

/** 抽象支付薪资方式*/
public interface PaymentMethod {void pay(Paycheck payCheck);
}

银行卡支付方式:

/** 银行卡支付方式实现类*/
public class BankMethod implements PaymentMethod {private String bank;//银行卡private double account;//应支付的工资public BankMethod(String bank, double account) {super();this.bank = bank;this.account = account;}public void pay(Paycheck payCheck) {// TODO Auto-generated method stubSystem.out.println("向银行卡"+bank+"支付"+payCheck.getNetPay()+"元钱");}@Overridepublic String toString() {return "BankMethod [bank=" + bank + ", account=" + account + "]";}}

到财务/出纳自取方式:

public class HoldMethod implements PaymentMethod {public void pay(Paycheck payCheck) {// TODO Auto-generated method stubSystem.out.println("到财务自取");}@Overridepublic String toString() {return "HoldMethod [到财务自取]";}}

邮寄方式:

/** 邮寄支付工资方式实现类*/
public class MailMethod implements PaymentMethod {private String address;public MailMethod(String address) {super();this.address = address;}public void pay(Paycheck payCheck) {// TODO Auto-generated method stubSystem.out.println("给"+address+"邮寄工资");}@Overridepublic String toString() {return "MailMethod [address=" + address + "]";}}

当我们继续理解需求,我们会发现小时工的薪水发放是每周五进行,固定薪资员工的薪水发放是每月月底,销售员工则是隔一周的周五,薪水的发放时间不一致,就需要对每种类型员工的发放时间进行单独计算,抽象出支付时间:

/** 抽象出支付时间*/
public interface PaymentSchedule {boolean isPayDate(LocalDate date);LocalDate getPayPeriodStartDate(LocalDate date);
}

该接口需实现两个方法,判断当前日子是否为支付日,返回上一次的支付日期。
每周周五进行支付:

/** 按每周周五进行支付实现类*/
public class WeeklySchedule implements PaymentSchedule {public boolean isPayDate(LocalDate date) {// TODO Auto-generated method stubreturn DateUtil.isFriday(date);}public LocalDate getPayPeriodStartDate(LocalDate date) {// TODO Auto-generated method stubreturn DateUtil.add(date, -6);}@Overridepublic String toString() {return "WeeklySchedule [每周五支付]";}}

月底支付:

/** 按月进行支付*/
public class MothlySchedule implements PaymentSchedule {public boolean isPayDate(LocalDate date) {// TODO Auto-generated method stubreturn DateUtil.isLastDayOfMonth(date);}public LocalDate getPayPeriodStartDate(LocalDate date) {// TODO Auto-generated method stubreturn DateUtil.getFirstDay(date);}@Overridepublic String toString() {return "MothlySchedule [月底支付]";}}

隔周周五支付:

/** 隔周周五进行支付*/
public class BiweeklySchedule implements PaymentSchedule {LocalDate firstPayableFriday = LocalDate.of(2019, 1, 4);public boolean isPayDate(LocalDate date) {// TODO Auto-generated method stubreturn DateUtil.isAfterFirday(date);}public LocalDate getPayPeriodStartDate(LocalDate date) {// TODO Auto-generated method stubreturn DateUtil.add(date, -13);}@Overridepublic String toString() {return "BiweeklySchedule [隔周周五支付]";}}

最后还有员工的会费计算,需要在计算薪资时扣除:

/** 抽象会费计算,协会对象*/
public interface Affiliation {double calculateDecutions(Paycheck paycheck);
}

这里实现了一个协会:

/** 具体协会实现类,负责计算应扣除项*/
public class UnionAffiliation implements Affiliation {private String memberId;private double weeklyBue;//每周固定应付款字段private List<ServiceChange> serviceChanges=new ArrayList<ServiceChange>();//不定时收的服务费public UnionAffiliation(String memberId, double weeklyBue) {super();this.memberId = memberId;this.weeklyBue = weeklyBue;}public String getMemberId() {return memberId;}public void setMemberId(String memberId) {this.memberId = memberId;}public double getWeeklyBue() {return weeklyBue;}public void setWeeklyBue(double weeklyBue) {this.weeklyBue = weeklyBue;}public void addServiceChanges(ServiceChange sc) {this.serviceChanges.add(sc);}public double calculateDecutions(Paycheck paycheck) {// TODO Auto-generated method stubint fridays=NumberOfFridaysInPayPeriod(paycheck.getPayPeriodStart(),paycheck.getPayPeriodend());double totalDue=fridays*weeklyBue;double totalChange=0;for(ServiceChange sc:serviceChanges) {if(DateUtil.between(sc.getDate(), paycheck.getPayPeriodStart(), paycheck.getPayPeriodend()))totalChange+=sc.getAmount();}return totalDue+totalChange;}private int NumberOfFridaysInPayPeriod(LocalDate start,LocalDate end) {ZonedDateTime zonedDatestart = start.atStartOfDay(ZoneId.systemDefault());ZonedDateTime zonedDateend = end.atStartOfDay(ZoneId.systemDefault());Date payPeriodStart=Date.from(zonedDatestart.toInstant());Date payPeriodEnd=Date.from(zonedDateend.toInstant());Calendar dayCa = Calendar.getInstance();dayCa.setTime(payPeriodStart);Calendar payPeriodEndCa = Calendar.getInstance();dayCa.setTime(payPeriodEnd);int fridays = 0;while (dayCa.before(payPeriodEndCa)) {if (dayCa.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) {fridays++;}dayCa.add(Calendar.DATE, 1);}return fridays;}@Overridepublic String toString() {return "UnionAffiliation [memberId=" + memberId + ", weeklyBue=" + weeklyBue + "]";}}

OK,再回过头来看下员工类的实现,成员变量里对于会费的计算、薪资策略的计算、发放薪水方式、发放薪水时间计算都做了抽象,将发放薪水过程的各个方法都分给了不同的接口、类进行处理,类与类间做到了低耦合、高内聚的设计思路。

    private String id;private String name;private String address;private List<Affiliation> affiliations=new ArrayList<Affiliation>();private PaymentClassification classification;private PaymentMethod paymentMethod;private PaymentSchedule schedule;

在计算发放薪资时:

public void payDay(Paycheck pc) {//发放薪水double grossPay=classification.calculatePay(pc);//薪水计算交由支付策略类计算double deductions=calculateDeductions(pc);//应扣除项交由会费类处理double netPay=grossPay-deductions;pc.setDeductions(deductions);pc.setEmpId(id);pc.setGrossPay(grossPay);pc.setNetPay(netPay);paymentMethod.pay(pc);//薪资发放交由员工选择的发放形式处理}protected double calculateDeductions(Paycheck pc) {double deductions=0;for(Affiliation affiliation:affiliations) {deductions+=affiliation.calculateDecutions(pc);}return deductions;}

整个过程类与类间的分工明确,这里还有将发放的薪资进行了封装:

public class Paycheck {//封装应支付薪水类private LocalDate payPeriodStart;//薪水起始计算时间private LocalDate payPeriodend;//薪水结束计算时间private double grossPay;//应付private double deductions;//扣除private double netPay;//实付private String empId;public Paycheck(LocalDate payperiodStart,LocalDate payPeriodend) {this.payPeriodStart=payperiodStart;this.payPeriodend=payPeriodend;}}

``

总结下,有人说薪水支付案例是最好的面向对象设计案例,通过这个案例的学习可以了解什么是面向接口编程而不是面向实现编程,如何设计把合适的功能分给合适的类去实现,个人觉得这个是策略设计模式非常好的运用,把代码逻辑封装到具有共同接口的独立的类中。
最后贴个自己写的本项目Github地址:

本项目Github地址

敏捷软件开发-薪水支付案例学习相关推荐

  1. 基于DotNet构件技术的企业级敏捷软件开发平台 - AgileEAS.NET - 文章汇总及学习指南...

    一.AgileEAS.NET平台简介 AgileEAS.NET平台是一套应用系统快速开发平台,用于帮助中小软件开发商快速构建自己的企业信息管理类开发团队,以达到节省开发成本.缩短开发时间,快速适应市场 ...

  2. 敏捷软件开发:原则、模式与实践pdf

    下载地址:网盘下载 内容简介  · · · · · · 在本书中,享誉全球的软件开发专家和软件工程大师Robert C.Martin将向您展示如何解决软件开发人员.项目经理及软件项目领导们所面临的最棘 ...

  3. 敏捷软件开发(c#版)文摘

    第一部分 敏捷开发 第1章 敏捷实践 第2章 极限编程概述 第3章 计划 第4章 测试 第5章 重构 第6章 一次编程实践 第二部分 敏捷设计 第7章 什么是敏捷设计 第8章 SRP 第9章 OCP ...

  4. 敏捷软件开发--敏捷宣言

    敏捷软件开发宣言 我们正在通过亲身实践以及帮助他人实践,提示更好的软件开发方法. 通过这项工作,我们认为: 人和交互   重于    过程和工具    可以工作的软件   重于   面面俱到的文档   ...

  5. 敏捷软件开发的12个原则

    作为一个软件工程师,软件设计和开发是最重要的技能,但是,从整个产品的角度上讲,项目管理能力比开发能力更重要,本文摘自Robert大叔的<敏捷软件开发>,粗体是Robert大叔的话,细体是我 ...

  6. 敏捷软件开发实践——估算与计划02

    目录 一.使用故事点估算大小 1.故事点是相对的 2.速度 3.小结 二.使用理想人天进行估算 1.理想时间和软件开发 2.以理想人天作为对大小的度量 3.给出一个而不是多个估算值 4.小结 三.估算 ...

  7. 敏捷软件开发实践——估算与计划(01)

    目录 一.计划的目的 1.为什么要进行估算和计划 2.优秀的计划是什么 3.敏捷计划是什么 4.小结 二.计划失败的原因 1.基于活动而不是基于特性进行计划 1.1.活动不会提前完成 1.2.延误沿着 ...

  8. 敏捷软件开发之何为敏捷开发

    敏捷开发,Agile Development,就是指能够在需求迅速变化的情况下快速开发软件.我们接触最多敏捷实践方式有:极限编程(XP).结对编程.测试驱动开发(TDD)等. 追究敏捷的历史,就必须要 ...

  9. 《敏捷软件开发:原则、模式与实践(C#版.修订版)》—第1章1.4节参考文献

    本节书摘来自异步社区<敏捷软件开发:原则.模式与实践(C#版.修订版)>一书中的第1章1.4节参考文献,作者[美]Robert C. Martin , Micah Martin,更多章节内 ...

  10. 软件开发计划_敏捷软件开发实践:估算与计划读书笔记113第11章 确定渴望度优先级...

    <敏捷软件开发实践:估算与计划>第11章 确定渴望度优先级,重点和要点的思维导图及文字内容. 第11章 确定渴望度优先级 If you have a choice of two thing ...

最新文章

  1. ASP.NET弹出对话框几种基本方法【】
  2. go实现重新给metric打标签上传到prometheus_案例分析|云原生监控Prometheus对样本rate计算,出现标签重复?...
  3. beego1---beego,bee环境配置
  4. gview java_java - 如何在干净模式下运行eclipse? 如果我们这样做会发生什么?
  5. Name node is in safe mode解决
  6. eclipse配置PHP自动提示代码
  7. 菜鸟学ASP.NET MVC4入门笔记
  8. 如何使用JPA和Hibernate映射JSON集合
  9. lpr命令linux下未找到,linux – LPR命令无法识别CUPS打印机
  10. c语言100阶乘的代码,求10000的阶乘(c语言代码实现)
  11. J2EE中EL表达式
  12. 小程序开发工具不显示tobar图标
  13. 1899元起!iQOO Z5造梦空间配色明日正式开售
  14. androidStudio项目删除模块后报错解决办法
  15. 企业基础管理薄弱,激励机制不健全怎么办?
  16. 使用 cookie的一些缺陷和隐患
  17. c++中DLL文件的编写与实现——三步走
  18. STEAM 教育相关书籍
  19. 双系统windows+linux如何正确删除linux
  20. 摘抄整理:基于数据驱动的故障诊断方法综述

热门文章

  1. 示波器的主要功能 示波器作用介绍
  2. css 响应式布局(媒体查询),兼容pc,ipad,移动端的布局单位
  3. 字节跳动的“飞阅会”开会模式:先笔谈 后PK 直接生成会议纪要
  4. M1增速还能指出A股底部吗?
  5. 【iOS】file not found: .../Build/Products/Debug-iphonesimulator file not found
  6. vue 移动端弹窗后禁止页面滚动 @touchmove.prevent
  7. 《人生七年》纪录片中问的问题
  8. syn重发_TCP/IP中SYN,FIN的缩写意思
  9. Cocos Creator性能调优优化集锦
  10. 数据科学和数学建模_数据建模 数据科学