設計模式之模板方法模式

模板方法模式定義如下:

定義一個操作中的算法框架,而將一些步驟的邏輯實現延遲到子類中。使得子類可以不改變一個算法的結構即可重新定義該算法的某些邏輯。


模板方法通用類圖如下:



模板方法是一個很簡單的實現模式,相信大家在寫代碼的過程中也遇到過或者自己寫過,只是可能不知道其實那就是模板方法模式,模板方法模式核心就是使用java的繼承機制,將某些步驟的邏輯交由子類去實現。


模板方法模式通用類圖代碼實現如下:


/**
 * 模板方法抽象類
 * 
 */
public abstract class AbstractClass {

    /**
     * 一般模板方法都用final修飾防止子類修改
     */
    public final void templateMethod() {

        // 執行邏輯1
        operation1();

        // 執行邏輯2
        operation2();
    }

    /**
     * 邏輯1
     */
    public abstract void operation1();

    /**
     * 邏輯2
     */
    public abstract void operation2();
}

/**
 * 模板方法類的子類,負責實現一些算法邏輯
 * 
 */
public class ConcreteClass1 extends AbstractClass {

    /**
     * 邏輯
     */
    @Override
    public void operation1() {

        System.out.println("文件校驗....");
    }

    /**
     * 
     * 邏輯1
     */
    @Override
    public void operation2() {

        System.out.println("數據校驗....");
    }

}

模板方法的代碼大致就是這樣,模板方法可以有多個子類,對應不同的算法邏輯,模板方法使用如下,客戶端依賴抽象即可,通過調用抽象類中的模板方法即可,如下:


public class Client {

    public static void main(String[] args) {
        
        AbstractClass abstractClass=new ConcreteClass1();
        
        abstractClass.templateMethod();
    }
}

下面介紹一個實際應用,在我們的開發項目中有很多數據文件的導入操作,例如:商戶數據導入,商戶數據分爲很多種:財付通數據、銀聯數據、快錢數據等等,但是所有數據都分爲兩步:

1.文件級校驗

2.數據級校驗

因爲上述數據導入成功後都會進入同一張商戶明細表,只是各個商戶數據的校驗規則不同,那麼我就可以使用模板方法模式實現。


商戶數據導入的類圖如下:





代碼如下

/**
 * 商戶數據導入的抽象類
 * 
 */
public abstract class MerchantDataImport {

    public final void dataImport(File file) {

        Boolean flag = false;
        // 文件級校驗
        flag = fileVoliate(file);

        if (!flag) {
            // 返回,提示用戶文件級校驗失敗
        } else {
            // 數據級校驗
            flag = dataVolidate(file);

            if (flag) {
                // 將數據保存數據庫
                System.out.println("保存數據中.....");
            } else {
                // 返回提示用戶,數據級校驗失敗,例如:XXX字段爲空
            }
        }

    }

    /**
     * 文件級校驗
     * 
     * @return
     */
    public abstract boolean fileVoliate(File file);

    /**
     * 數據級校驗
     * 
     * @return
     */
    public abstract boolean dataVolidate(File file);
}

/**
 * 財付通商戶數據導入
 * 
 */
public class TpayDataImport extends MerchantDataImport {

    /**
     * 文件級校驗
     * 
     * @param file
     * @return
     */
    @Override
    public boolean fileVoliate(File file) {
        System.out.println("財付通數據文件級校驗中....");
        return true;
    }

    /**
     * 數據級校驗
     * 
     * @param file
     * @return
     */
    @Override
    public boolean dataVolidate(File file) {
        System.out.println("財付通數據級校驗中.....");
        return true;
    }

}

public class ApayDataImport extends MerchantDataImport {

    /**
     * 文件級校驗
     * 
     * @param file
     * @return
     */
    @Override
    public boolean fileVoliate(File file) {
        System.out.println("Apay數據文件級校驗中....");
        return true;
    }

    /**
     * 數據級校驗
     * 
     * @param file
     * @return
     */
    @Override
    public boolean dataVolidate(File file) {
        System.out.println("Apay數據級校驗中.....");
        return true;
    }

}

public class YpayDataImport extends MerchantDataImport {

    /**
     * 文件級校驗
     * 
     * @param file
     * @return
     */
    @Override
    public boolean fileVoliate(File file) {
        System.out.println("Ypay數據文件級校驗中....");
        return true;
    }

    /**
     * 數據級校驗
     * 
     * @param file
     * @return
     */
    @Override
    public boolean dataVolidate(File file) {
        System.out.println("Ypay數據級校驗中.....");
        return true;
    }

}

使用方式如下:


/**
 * 
 * 商戶數據導入的控制器
 * 
 */
public class MerchantDataImportAction {

    private File file;
    private String fileFileName;

    private String payCode;

    private MerchantDataImport merchantDataImport;

    /**
     * 商戶數據導入
     * 
     * @return
     */
    public String merchantDataImport() {

        if ("TPAY".equalsIgnoreCase(payCode)) {
            // 財付通數據,應該使用注入方式,交由Spring注入,此處只是模擬
            merchantDataImport = new TpayDataImport();

            merchantDataImport.dataImport(file);
        }

        if ("APAY".equalsIgnoreCase(payCode)) {
            // 財付通數據,應該使用注入方式,交由Spring注入,此處只是模擬
            merchantDataImport = new ApayDataImport();

            merchantDataImport.dataImport(file);
        }

        if ("YPAY".equalsIgnoreCase(payCode)) {
            // 財付通數據,應該使用注入方式,交由Spring注入,此處只是模擬
            merchantDataImport = new YpayDataImport();

            merchantDataImport.dataImport(file);
        }

        return "merchantDataImport";
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    public String getFileFileName() {
        return fileFileName;
    }

    public void setFileFileName(String fileFileName) {
        this.fileFileName = fileFileName;
    }

}

由於項目開發中,商戶不止以上三家,有很多家商戶,這樣的話,我們可以結合工廠方法模式對其進行改造一下,改造後類圖如下:





代碼如下:


/**
 * 商戶導入數據的工廠類
 * 
 */
public abstract class MerchantDataImportAbstractFactory {

    /**
     * 生產商戶導入數據類
     * 
     * @return
     */
    public abstract MerchantDataImport createMerchantDataImport(String payCode);
}

public class MerchantDataImportFactory extends MerchantDataImportAbstractFactory {

    // 配置商戶數據導入類,MerchantDataImportFactory通過spring注入時,其依賴的類也會被注入,每增加一個商戶可以在此添加
    private TpayDataImport tpayDataImport;

    private YpayDataImport ypayDataImport;

    private ApayDataImport apayDataImport;

    /**
     * @return
     */
    @Override
    public MerchantDataImport createMerchantDataImport(String payCode) {
        
        if("TPAY".equalsIgnoreCase(payCode)){
            return tpayDataImport;
        }
        

        if("YPAY".equalsIgnoreCase(payCode)){
            return ypayDataImport;
        }
        

        if("APAY".equalsIgnoreCase(payCode)){
            return apayDataImport;
        }
        
        return null;
    }

}

Action類修改如下:


public class MerchantDataImportAction {

    private File file;
    private String fileFileName;

    private String payCode;

    private MerchantDataImportAbstractFactory factory;

    /**
     * 商戶數據導入
     * 
     * @return
     */
    public String merchantDataImport() {


        MerchantDataImport merchantDataImport=factory.createMerchantDataImport(payCode);
        
        //調用模板方法
        merchantDataImport.dataImport(file);
        
        return "merchantDataImport";
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    public String getFileFileName() {
        return fileFileName;
    }

    public void setFileFileName(String fileFileName) {
        this.fileFileName = fileFileName;
    }
    
    

}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章