设计模式之模板方法模式

模板方法模式定义如下:

定义一个操作中的算法框架,而将一些步骤的逻辑实现延迟到子类中。使得子类可以不改变一个算法的结构即可重新定义该算法的某些逻辑。


模板方法通用类图如下:



模板方法是一个很简单的实现模式,相信大家在写代码的过程中也遇到过或者自己写过,只是可能不知道其实那就是模板方法模式,模板方法模式核心就是使用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;
    }
    
    

}



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