天天看點

操作複雜對象結構——通路者模式(三)

26.3 完整解決方案

      Sunny軟體公司開發人員使用通路者模式對OA系統中員工資料彙總子產品進行重構,使得系統可以很友善地增加新類型的通路者,更加符合“單一職責原則”和“開閉原則”,重構後的基本結構如圖26-3所示:

操作複雜對象結構——通路者模式(三)

        在圖26-3中,FADepartment表示财務部,HRDepartment表示人力資源部,它們充當具體通路者角色,其抽象父類Department充當抽象通路者角色;EmployeeList充當對象結構,用于存儲員工清單;FulltimeEmployee表示正式員工,ParttimeEmployee表示臨時工,它們充當具體元素角色,其父接口Employee充當抽象元素角色。完整代碼如下所示:

import java.util.*;

//員工類:抽象元素類
interface Employee
{
	public void accept(Department handler); //接受一個抽象通路者通路
}

//全職員工類:具體元素類
class FulltimeEmployee implements Employee
{
	private String name;
	private double weeklyWage;
	private int workTime;

	public FulltimeEmployee(String name,double weeklyWage,int workTime)
	{
		this.name = name;
		this.weeklyWage = weeklyWage;
		this.workTime = workTime;
	}	

	public void setName(String name) 
    {
		this.name = name; 
	}

	public void setWeeklyWage(double weeklyWage) 
    {
		this.weeklyWage = weeklyWage; 
	}

	public void setWorkTime(int workTime) 
    {
		this.workTime = workTime; 
	}

	public String getName() 
    {
		return (this.name); 
	}

	public double getWeeklyWage() 
    {
		return (this.weeklyWage); 
	}

	public int getWorkTime() 
    {
		return (this.workTime); 
	}

	public void accept(Department handler)
    {
		handler.visit(this); //調用通路者的通路方法
	}
}

//兼職員工類:具體元素類
class ParttimeEmployee implements Employee
{
	private String name;
	private double hourWage;
	private int workTime;

	public ParttimeEmployee(String name,double hourWage,int workTime)
	{
		this.name = name;
		this.hourWage = hourWage;
		this.workTime = workTime;
	}	

	public void setName(String name) 
    {
		this.name = name; 
	}

	public void setHourWage(double hourWage) 
    {
		this.hourWage = hourWage; 
	}

	public void setWorkTime(int workTime) 
    {
		this.workTime = workTime; 
	}

	public String getName() 
    {
		return (this.name); 
	}

	public double getHourWage() 
    {
		return (this.hourWage); 
	}

	public int getWorkTime() 
    {
		return (this.workTime); 
	}

	public void accept(Department handler)
    {
		handler.visit(this); //調用通路者的通路方法
	}
}

//部門類:抽象通路者類
abstract class Department
{
    //聲明一組重載的通路方法,用于通路不同類型的具體元素
	public abstract void visit(FulltimeEmployee employee);
	public abstract void visit(ParttimeEmployee employee);	
}

//财務部類:具體通路者類
class FADepartment extends Department
{
    //實作财務部對全職員工的通路
	public void visit(FulltimeEmployee employee)
	{
		int workTime = employee.getWorkTime();
		double weekWage = employee.getWeeklyWage();
		if(workTime > 40)
		{
			weekWage = weekWage + (workTime - 40) * 100;
		}
		else if(workTime < 40)
		{
			weekWage = weekWage - (40 - workTime) * 80;
			if(weekWage < 0)
			{
				weekWage = 0;
			}
		}
		System.out.println("正式員工" + employee.getName() + "實際工資為:" + weekWage + "元。");			
	}

    //實作财務部對兼職員工的通路
	public void visit(ParttimeEmployee employee)
	{
		int workTime = employee.getWorkTime();
		double hourWage = employee.getHourWage();
		System.out.println("臨時工" + employee.getName() + "實際工資為:" + workTime * hourWage + "元。");		
	}		
}

//人力資源部類:具體通路者類
class HRDepartment extends Department
{
    //實作人力資源部對全職員工的通路
	public void visit(FulltimeEmployee employee)
	{
		int workTime = employee.getWorkTime();
		System.out.println("正式員工" + employee.getName() + "實際工作時間為:" + workTime + "小時。");
		if(workTime > 40)
		{
			System.out.println("正式員工" + employee.getName() + "加班時間為:" + (workTime - 40) + "小時。");
		}
		else if(workTime < 40)
		{
			System.out.println("正式員工" + employee.getName() + "請假時間為:" + (40 - workTime) + "小時。");
		}						
	}

    //實作人力資源部對兼職員工的通路
	public void visit(ParttimeEmployee employee)
	{
		int workTime = employee.getWorkTime();
		System.out.println("臨時工" + employee.getName() + "實際工作時間為:" + workTime + "小時。");
	}		
}

//員工清單類:對象結構
class EmployeeList
{
    //定義一個集合用于存儲員工對象
	private ArrayList<Employee> list = new ArrayList<Employee>();

	public void addEmployee(Employee employee)
	{
		list.add(employee);
	}

    //周遊通路員工集合中的每一個員工對象
	public void accept(Department handler)
	{
		for(Object obj : list)
		{
			((Employee)obj).accept(handler);
		}
	}
}
           

      為了提高系統的靈活性和可擴充性,我們将具體通路者類的類名存儲在配置檔案中,并通過工具類XMLUtil來讀取配置檔案并反射生成對象,XMLUtil類的代碼如下所示:

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
class XMLUtil
{
    //該方法用于從XML配置檔案中提取具體類類名,并傳回一個執行個體對象
    public static Object getBean()
    {
		try
		{
			//建立文檔對象
			DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = dFactory.newDocumentBuilder();
			Document doc;							
			doc = builder.parse(new File("config.xml")); 
		
			//擷取包含類名的文本節點
			NodeList nl = doc.getElementsByTagName("className");
            Node classNode=nl.item(0).getFirstChild();
            String cName=classNode.getNodeValue();
            
            //通過類名生成執行個體對象并将其傳回
            Class c=Class.forName(cName);
	  	    Object obj=c.newInstance();
            return obj;
        }   
        catch(Exception e)
        {
           	e.printStackTrace();
           	return null;
       	}
    }
}
           

       配置檔案config.xml中存儲了具體通路者類的類名,代碼如下所示:

<?xml version="1.0"?>
<config>
	<className>FADepartment</className>
</config>
           

     編寫如下用戶端測試代碼:

class Client
{
	public static void main(String args[])
	{
		EmployeeList list = new EmployeeList();
		Employee fte1,fte2,fte3,pte1,pte2;

		fte1 = new FulltimeEmployee("張無忌",3200.00,45);
		fte2 = new FulltimeEmployee("楊過",2000.00,40);
		fte3 = new FulltimeEmployee("段譽",2400.00,38);
		pte1 = new ParttimeEmployee("洪七公",80.00,20);
		pte2 = new ParttimeEmployee("郭靖",60.00,18);

		list.addEmployee(fte1);
		list.addEmployee(fte2);
		list.addEmployee(fte3);
		list.addEmployee(pte1);
		list.addEmployee(pte2);

		Department dep;
		dep = (Department)XMLUtil.getBean();
		list.accept(dep);
	}
}
           

      編譯并運作程式,輸出結果如下:

正式員工張無忌實際工資為:3700.0元。

正式員工楊過實際工資為:2000.0元。

正式員工段譽實際工資為:2240.0元。

臨時工洪七公實際工資為:1600.0元。

臨時工郭靖實際工資為:1080.0元。

      如果需要更換具體通路者類,無須修改源代碼,隻需修改配置檔案,例如将通路者類由财務部改為人力資源部,隻需将存儲在配置檔案中的具體通路者類FADepartment改為HRDepartment,如下代碼所示:

<?xml version="1.0"?>
<config>
    <className>HRDepartment</className>
</config>
           

      重新運作用戶端程式,輸出結果如下:

正式員工張無忌實際工作時間為:45小時。

正式員工張無忌加班時間為:5小時。

正式員工楊過實際工作時間為:40小時。

正式員工段譽實際工作時間為:38小時。

正式員工段譽請假時間為:2小時。

臨時工洪七公實際工作時間為:20小時。

臨時工郭靖實際工作時間為:18小時。

      如果要在系統中增加一種新的通路者,無須修改源代碼,隻要增加一個新的具體通路者類即可,在該具體通路者中封裝了新的操作元素對象的方法。從增加新的通路者的角度來看,通路者模式符合“開閉原則”。

      如果要在系統中增加一種新的具體元素,例如增加一種新的員工類型為“退休人員”,由于原有系統并未提供相應的通路接口(在抽象通路者中沒有聲明任何通路“退休人員”的方法),是以必須對原有系統進行修改,在原有的抽象通路者類和具體通路者類中增加相應的通路方法。從增加新的元素的角度來看,通路者模式違背了“開閉原則”。

      綜上所述,通路者模式與抽象工廠模式類似,對“開閉原則”的支援具有傾斜性,可以很友善地添加新的通路者,但是添加新的元素較為麻煩。

【作者:劉偉 http://blog.csdn.net/lovelion】

繼續閱讀