本例完整程序下载:
http://www.blogjava.net/Files/sitinspring/TaxCaculator20071025203159.rar
世界天天在变,程序也一样,唯一不变的只有变化二字.现代程序应该随着日新月异的环境而不断变化,此之谓"物竞天择,适者生存",下面的例子就演示了这一变化过程.
需求如下:(注:非真实税率,仅仅是个例子)
税后薪水=月总收入×适用税率-速算扣除数
级 数 全月应纳税所得额 税率(%) 税率 速算扣除数
1 不超过500元的 5%
2 超过500元至2000元的部分 10% 25
3 超过2000元至5000元的部分 15% 125
4 超过5000元至20000元的部分 20% 375
5 超过20000元的部分 25% 1375
如果让你来为下列需求书写一段代码,你将如何处理
1.过程式方法:
如果说需求(税率和扣除数)在较长一段时间内不会变化,也就是说需求变化时,程序生命已经结束,我们可以使用传统的分支语句来编码,程序写出来极简单.
package com.sitinspring.processstyle;
/** *//**
* Main函数所在的类
* @author sitinspring(junglesong@gmail.com)
*
*/
public class Main{
public static void main(String[] args){
for(double total=100;total<30000;total+=1000){
System.out.println("税前薪水="+total+" 税后薪水="+getSalary(total));
}
}
// 传统的过程式计算方法
public static double getSalary(double total){
double retval;
if(total < 500){
retval=total*(1-0.05);
}
else if(total < 2000){
retval=total*(1-0.1)-25;
}
else if(total < 5000){
retval=total*(1-0.15)-125;
}
else if(total < 20000){
retval=total *(1-0.20)-375;
}
else{
retval=total *(1-0.25)-1375;
}
return retval;
}
}
2.
OO化的方法
但是如果说需求(税率和扣除数)在软件生命已经结束前是有可能变化的,或者说变化不可预期,用户一定程序上也希望自己修改,IF分支的结构将不再适用,但程序可以停止,这时就应该把分支语句OO化,将规则和数据分离开来可以了,至于数据是固化在程序中还是启动时载入可以视情况确定,例子中采取了固化的方式,一般来说用XML文件记录数据较好,改起来很方便.
Main类:
package com.sitinspring.oostyle;
/** *//**
* Main函数所在的类
* @author sitinspring(junglesong@gmail.com)
*
*/
public class Main{
public static void main(String[] args){
SalaryCaculator caculator=new SalaryCaculator();
for(double total=100;total<30000;total+=1000){
System.out.println("税前薪水="+total+" 税后薪水="+caculator.getSalaryAfterTax(total));
}
}
}
SalaryCaculator类(其中99999999表示极大值,一般收入都小于这个值):
package com.sitinspring.oostyle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/** *//**
* 工资计算类
* @author sitinspring(junglesong@gmail.com)
*
*/
public class SalaryCaculator{
private List<SalaryGrade> grades;
public SalaryCaculator(){
grades=new ArrayList<SalaryGrade>();
grades.add(new SalaryGrade(500,0.05,0));
grades.add(new SalaryGrade(2000,0.1,25));
grades.add(new SalaryGrade(5000,0.15,125));
grades.add(new SalaryGrade(20000,0.20,375));
grades.add(new SalaryGrade(99999999,0.25,1375));
}
// OO化的查询方法
public double getSalaryAfterTax(double total){
SalaryGrade taxGrade=null;
for(Iterator it=grades.iterator();it.hasNext();){
taxGrade=(SalaryGrade)it.next();
if(total>taxGrade.getGrade()){
continue;
}
else{
break;
}
}
return total*(1-taxGrade.getRatio())-taxGrade.getDiscount();
}
}
SalaryGrade类:
package com.sitinspring.oostyle;
/** *//**
* 工资等级类
* @author sitinspring(junglesong@gmail.com)
*
*/
public class SalaryGrade {
// 月薪界限
private double grade;
// 税率
private double ratio;
// 折扣
private double discount;
public SalaryGrade(double grade, double ratio, double discount) {
this.grade = grade;
this.ratio = ratio;
this.discount = discount;
}
public SalaryGrade() {
this(0.0f, 0.0f, 0.0f);
}
public double getDiscount() {
return discount;
}
public double getGrade() {
return grade;
}
public double getRatio() {
return ratio;
}
}
3.
线程安全的OO方法
如果说需求(税率和扣除数)是人为动态指定的,且整个系统是7*24小时的系统,一般不允许停机.这样的情况下要求程序具有热插拔的能力,这时,我们必须加上一个读写锁才能解决问题.
Main类:
package com.sitinspring.dynamicoostyle;
import java.util.ArrayList;
import java.util.List;
import com.sitinspring.oostyle.SalaryGrade;
/** *//**
* Main函数所在的类
*
* @author sitinspring(junglesong@gmail.com)
*
*/
public class Main {
public static void main(String[] args) {
SalaryCaculator caculator = new SalaryCaculator();
List<SalaryGrade> grades1 = new ArrayList<SalaryGrade>();
grades1.add(new SalaryGrade(500, 0.05, 0));
grades1.add(new SalaryGrade(2000, 0.1, 25));
grades1.add(new SalaryGrade(5000, 0.15, 125));
grades1.add(new SalaryGrade(20000, 0.20, 375));
grades1.add(new SalaryGrade(99999999, 0.25, 1375));
Writer writer1=new Writer(caculator,grades1);
List<SalaryGrade> grades2 = new ArrayList<SalaryGrade>();
grades2.add(new SalaryGrade(5000, 0.5, 125));
grades2.add(new SalaryGrade(99999999, 0.9, 1375));
Writer writer2=new Writer(caculator,grades2);
while (true) {
for (double total = 100; total < 30000; total += 1000) {
sleep(1);
System.out.println("税前薪水=" + total + " 税后薪水="
+ caculator.getSalaryAfterTax(total));
}
}
}
// 用于延时
public static void sleep(int sleepSecond){
try{
Thread.sleep(sleepSecond*1000);
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
ReadWriteLock类:
package com.sitinspring.dynamicoostyle;
/** *//**
* 读写锁,用于线程控制
* @author sitinspring(junglesong@gmail.com)
*
*/
public class ReadWriteLock{
// 读状态
private boolean isRead;
// 写状态
private boolean isWrite;
public synchronized void readLock(){
// 有写入时读取线程停止
while(isWrite){
try{
System.out.println("有线程在进行写入,读取线程停止,进入等待状态");
wait();
}
catch(InterruptedException ex){
ex.printStackTrace();
}
}
System.out.println("设定锁为读取状态");
isRead=true;
}
public synchronized void readUnlock(){
System.out.println("解除读取锁");
isRead=false;
notifyAll();
}
public synchronized void writeLock(){
// 有读取时读取线程停止
while(isRead){
try{
System.out.println("有线程在进行读取,写入线程停止,进入等待状态");
wait();
}
catch(InterruptedException ex){
ex.printStackTrace();
}
}
// 有写入时写入线程也一样要停止
while(isWrite){
try{
System.out.println("有线程在进行写入,写入线程停止,进入等待状态");
wait();
}
catch(InterruptedException ex){
ex.printStackTrace();
}
}
System.out.println("设定锁为写入状态");
isWrite=true;
}
public synchronized void writeUnlock(){
System.out.println("解除写入锁");
isWrite=false;
notifyAll();
}
}
SalaryCaculator类:
package com.sitinspring.dynamicoostyle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.sitinspring.oostyle.SalaryGrade;
/** *//**
* 工资计算类
*
* @author sitinspring(junglesong@gmail.com)
*
*/
public class SalaryCaculator {
private List<SalaryGrade> grades;
private ReadWriteLock readWriteLock;
public SalaryCaculator() {
readWriteLock = new ReadWriteLock();
grades = new ArrayList<SalaryGrade>();
grades.add(new SalaryGrade(500, 0.05, 0));
grades.add(new SalaryGrade(2000, 0.1, 25));
grades.add(new SalaryGrade(5000, 0.15, 125));
grades.add(new SalaryGrade(20000, 0.20, 375));
grades.add(new SalaryGrade(99999999, 0.25, 1375));
}
// 线程安全的,OO化的查询方法
public double getSalaryAfterTax(double total) {
try {
readWriteLock.readLock();
SalaryGrade taxGrade = null;
for (Iterator it = grades.iterator(); it.hasNext();) {
taxGrade = (SalaryGrade) it.next();
if (total > taxGrade.getGrade()) {
continue;
} else {
break;
}
}
return total * (1 - taxGrade.getRatio()) - taxGrade.getDiscount();
} finally {
readWriteLock.readUnlock();
}
}
public void setGrades(List<SalaryGrade> grades) {
try {
readWriteLock.writeLock();
this.grades = grades;
} finally {
readWriteLock.writeUnlock();
}
}
}
Writer类(模拟人工写入):
package com.sitinspring.dynamicoostyle;
import java.util.List;
import java.util.Random;
import com.sitinspring.oostyle.SalaryGrade;
/** *//**
* 更新工资等级的线程
*
* @author sitinspring(junglesong@gmail.com)
*
*/
public class Writer implements Runnable {
private static final Random random = new Random();
private SalaryCaculator caculator;
private List<SalaryGrade> grades;
public Writer(SalaryCaculator caculator, List<SalaryGrade> grades) {
this.caculator = caculator;
this.grades = grades;
Thread thread = new Thread(this);
thread.start();
}
public void run() {
while (true) {
try {
Thread.sleep(random.nextInt(3) * 1000);
} catch (Exception ex) {
ex.printStackTrace();
}
caculator.setGrades(grades);
}
}
}
虽然上述三段程序大小和复杂度各不一样,但它们没有优劣之分,各自都是适应环境的产物,没有一种程序结构能包打天下,但我们需要知道根据客观情况选取不同的结构,这样才能创造出优秀的程序.
以上