和风细雨

世上本无难事,心以为难,斯乃真难。苟不存一难之见于心,则运用之术自出。

从薪水计算的例子看一段程序在不同环境中的变化

本例完整程序下载: 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.0f0.0f0.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(5000.050));
        grades1.add(
new SalaryGrade(20000.125));
        grades1.add(
new SalaryGrade(50000.15125));
        grades1.add(
new SalaryGrade(200000.20375));
        grades1.add(
new SalaryGrade(999999990.251375));        
        Writer writer1
=new Writer(caculator,grades1);
        
        List
<SalaryGrade> grades2 = new ArrayList<SalaryGrade>();
        grades2.add(
new SalaryGrade(50000.5125));
        grades2.add(
new SalaryGrade(999999990.91375));        
        
        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(5000.050));
        grades.add(
new SalaryGrade(20000.125));
        grades.add(
new SalaryGrade(50000.15125));
        grades.add(
new SalaryGrade(200000.20375));
        grades.add(
new SalaryGrade(999999990.251375));
    }


    
// 线程安全的,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);
        }

    }

}

虽然上述三段程序大小和复杂度各不一样,但它们没有优劣之分,各自都是适应环境的产物,没有一种程序结构能包打天下,但我们需要知道根据客观情况选取不同的结构,这样才能创造出优秀的程序.

以上

posted on 2008-02-22 11:27 和风细雨 阅读(235) 评论(0)  编辑  收藏 所属分类: OOP


只有注册用户登录后才能发表评论。


网站导航: