本例完整程序下載:
http://m.tkk7.com/Files/sitinspring/TaxCaculator20071025203159.rar
世界天天在變,程序也一樣,唯一不變的只有變化二字.現(xiàn)代程序應(yīng)該隨著日新月異的環(huán)境而不斷變化,此之謂"物競天擇,適者生存",下面的例子就演示了這一變化過程.
需求如下:(注:非真實稅率,僅僅是個例子)
稅后薪水=月總收入×適用稅率-速算扣除數(shù)
級 數(shù) 全月應(yīng)納稅所得額 稅率(%) 稅率 速算扣除數(shù)
1 不超過500元的 5%
2 超過500元至2000元的部分 10% 25
3 超過2000元至5000元的部分 15% 125
4 超過5000元至20000元的部分 20% 375
5 超過20000元的部分 25% 1375
如果讓你來為下列需求書寫一段代碼,你將如何處理
1.過程式方法:
如果說需求(稅率和扣除數(shù))在較長一段時間內(nèi)不會變化,也就是說需求變化時,程序生命已經(jīng)結(jié)束,我們可以使用傳統(tǒng)的分支語句來編碼,程序?qū)懗鰜順O簡單.
package com.sitinspring.processstyle;


/** *//**
* Main函數(shù)所在的類
* @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));
}
}
// 傳統(tǒng)的過程式計算方法

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化的方法
但是如果說需求(稅率和扣除數(shù))在軟件生命已經(jīng)結(jié)束前是有可能變化的,或者說變化不可預(yù)期,用戶一定程序上也希望自己修改,IF分支的結(jié)構(gòu)將不再適用,但程序可以停止,這時就應(yīng)該把分支語句OO化,將規(guī)則和數(shù)據(jù)分離開來可以了,至于數(shù)據(jù)是固化在程序中還是啟動時載入可以視情況確定,例子中采取了固化的方式,一般來說用XML文件記錄數(shù)據(jù)較好,改起來很方便.
Main類:
package com.sitinspring.oostyle;


/** *//**
* Main函數(shù)所在的類
* @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方法
如果說需求(稅率和扣除數(shù))是人為動態(tài)指定的,且整個系統(tǒng)是7*24小時的系統(tǒng),一般不允許停機.這樣的情況下要求程序具有熱插拔的能力,這時,我們必須加上一個讀寫鎖才能解決問題.
Main類:
package com.sitinspring.dynamicoostyle;

import java.util.ArrayList;
import java.util.List;

import com.sitinspring.oostyle.SalaryGrade;


/** *//**
* Main函數(shù)所在的類
*
* @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
{
// 讀狀態(tài)
private boolean isRead;
// 寫狀態(tài)
private boolean isWrite;

public synchronized void readLock()
{
// 有寫入時讀取線程停止

while(isWrite)
{

try
{
System.out.println("有線程在進行寫入,讀取線程停止,進入等待狀態(tài)");
wait();
}

catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
System.out.println("設(shè)定鎖為讀取狀態(tài)");
isRead=true;
}

public synchronized void readUnlock()
{
System.out.println("解除讀取鎖");
isRead=false;
notifyAll();
}

public synchronized void writeLock()
{
// 有讀取時讀取線程停止

while(isRead)
{

try
{
System.out.println("有線程在進行讀取,寫入線程停止,進入等待狀態(tài)");
wait();
}

catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
// 有寫入時寫入線程也一樣要停止

while(isWrite)
{

try
{
System.out.println("有線程在進行寫入,寫入線程停止,進入等待狀態(tài)");
wait();
}

catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
System.out.println("設(shè)定鎖為寫入狀態(tài)");
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);
}
}
}
雖然上述三段程序大小和復(fù)雜度各不一樣,但它們沒有優(yōu)劣之分,各自都是適應(yīng)環(huán)境的產(chǎn)物,沒有一種程序結(jié)構(gòu)能包打天下,但我們需要知道根據(jù)客觀情況選取不同的結(jié)構(gòu),這樣才能創(chuàng)造出優(yōu)秀的程序.
以上