使用多态替换简单工厂

replace simple factory using polymorphism

我正在尝试替换简单工厂 StatsCreatorFactory.java class 以删除 switch case 语句的多次使用。这是我的情况:

StatsServlet.java

public class StatsServlet extends HttpServlet{ 

    private static final long serialVersionUID = 1L;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        StatsContext context = new StatsContext(request,response);
        **IStatsCreator creator = StatsCreatorFactory.getCreator(context);**
        IChart chart = creator.createChart();
        String jsonChart = creator.chartToJson(chart);
        creator.sendResponse(jsonChart);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processRequest(request, response);
    }

}

IStatsCreator.java

public interface IStatsCreator {

    public IChart createChart() throws IOException;
    public  IDetailsTable createDetailsTable(String itemSelected);

    public String chartToJson(IChart chart);
    public String tableToJson(IDetailsTable table);

    public void sendResponse(String resp) throws IOException;


    public List<File> findFiles() throws IOException, ParseException;
    public List<LogLine> parseFiles(List<File> files) throws IOException;
    public IFileIntervalDateDetector getFileDetector() throws IOException;

    public TargetChartOperation getTargetOperation();
    public IChart getChart();
    public IDetailsTable getDetailsTable();
}

AbstractStatsCreator

public abstract class AbstractStatsCreator implements IStatsCreator{

    protected StatsContext context;

    public AbstractStatsCreator(StatsContext context) {
        this.context = context;
    }

    protected abstract ILogParser getParser();
    protected abstract IStatsHelper getHelper();

    @Override
    public IFileIntervalDateDetector getFileDetector() throws IOException {
        IFileIntervalDateDetector fileDetector = new FileDetector();
        fileDetector.addPattern(new FileNamePattern(TypeSubjectEnum.valueOf(context.getSubject().toUpperCase()).getFilePattern()));
        fileDetector.addPattern(new FileNamePattern(context.getInstance()));
        return fileDetector;
    }

    @Override
    public final List<File> findFiles() throws IOException, ParseException{
        if(context.getDateStart().equalsIgnoreCase(StringUtils.EMPTY) && context.getDateEnd().equalsIgnoreCase(StringUtils.EMPTY)){
            return getFileDetector().findDailyFiles();
        }

        Date startDate = new SimpleDateFormat("ddMMyyyy").parse(context.getDateStart());
        Date stopDate = new SimpleDateFormat("ddMMyyyy").parse(context.getDateEnd());
        Date currentDate = new Date(System.currentTimeMillis());

        if(DateUtils.isSameDay(startDate, stopDate) && DateUtils.isSameDay(startDate, currentDate)){
            return getFileDetector().findDailyFiles();
        }

        return getFileDetector().findFilesByInterval(context.getDateStart(), context.getDateEnd()); 
    }

    @Override
    public final List<LogLine> parseFiles(List<File> files) throws IOException{
        return getParser().parseLogFiles(files);
    }

    @Override
    public IChart createChart() throws IOException{

        if(context.needUpdate()){
            List<File> files = null;
            try {
                files = findFiles();
            } catch (ParseException e) {
                files=Lists.newArrayList();
            }
            List<LogLine> logLines = parseFiles(files);
            context.setLogLines(logLines);
            context.updateContext(); 
        }

        IChart chart = getChart().create();
        return chart;
    }

    @Override
    public IDetailsTable createDetailsTable(String itemSelected) {
        IDetailsTable table = getDetailsTable().create(itemSelected);
        return table;
    }

    @Override
    public String chartToJson(IChart chart) {
        StringBuilder json = new StringBuilder(JsonTransformer.renderChart(chart));
        return json.toString();
    }

    @Override
    public String tableToJson(IDetailsTable table) {
        StringBuilder json = new StringBuilder(JsonTransformer.renderDetailsTable(table));
        return json.toString();
    }

    @Override
    public void sendResponse(String resp) throws IOException {
        context.getResponse().setContentType("application/json");
        PrintWriter out = context.getResponse().getWriter();
        out.write(resp.toString());
        out.flush();
    }

}

StatsCreatorFactory.java

public class StatsCreatorFactory {

    public static IStatsCreator getCreator(StatsContext context){

        if(context == null){
            throw new IllegalArgumentException("Context nullo");
        }

        IStatsCreator creator=null;

        switch (context.getOperation()) {
        case "validate":
            creator = new ValidateStatsCreator(context);
            break;
        case "extract":
            creator = new ExtractStatsCreator(context);
            break;
        case "transform":
            creator = new TransformStatsCreator(context);
            break;
        case "view":
            creator = new ViewStatsCreator(context);
            break;  
        default:
            creator = new GeneralStatsCreator(context);
            break;
        }

        return creator;
    }
}

我会尝试找到一种方法来实例化 ICreator classes,避免使用简单工厂 class,我可以使用任何重构或设计模式吗? 阅读 Martin Fawler 的书我想知道我是否可以使用多态性,但我找不到任何方法在我的代码中复制它。

我会首先尝试修改 IStatsCreator 和 AbstractStatsCreator 以获得无状态 bean。

在您的示例中,您只需删除在 AbstractStatsCreator 中定义为 class 变量的 StatsContext 上下文。因此上下文不应绑定到 class 实例。当在需要上下文的创建者上调用方法时,它应该从外部传入。为此,您可以重构 IStatsCreator 并将上下文添加到需要它的所有方法。

例如:

public IChart createChart() 抛出 IOException;

新:

public IChart createChart( StatsContext context ) 抛出 IOException;

等等。 之后,您不必为每个上下文调用创建 AbstractStatsCreator 实现的新实例。您只需要每种类型都有一个实例。这种类型的实例可以映射到 StatsCreatorFactory 中,并在需要时获取。我还建议摆脱静态方法。使 StatsCreatorFactory 成为一个真正的 bean,它更容易管理,也更容易被模拟以进行测试。:

public class StatsCreatorFactory {

    private Map<String, IStatsCreator> statsCreators = new HashMap<String, IStatsCreator>();

    public void registerStatsCreator( String type, IStatsCreator creator ) {
        statsCreators.put( type, creator );
    }

    public IStatsCreator getCreator( String type ){

        IStatsCreator creator= statsCreators.get( type );

        if(creator == null){
            throw new IllegalArgumentException("no creator registered for type : " + type);
        }

        return creator;
    }

}

最后,StatsCreatorFactory 更像是一个提供者,而不是一个工厂。也许您也可以将其重命名为 StatsCreatorProvider。

public class StatsServlet extneds HttpServlet{

private static final long serialVersionUID = 1L;

private StatsCreatorProvider statsCreatorProvider;

public void init() {
    statsCreatorProvider = new StatsCreatorProvider();
    statsCreatorProvider.registerStatsCreator( "validate", new ValidateStatsCreator() );
    statsCreatorProvider.registerStatsCreator( "extract" new ExtractStatsCreator() );
    ...
}

protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    StatsContext context = new StatsContext(request,response);
    IStatsCreator creator = statsCreatorProvider.getCreator( context.getOperation() );
    IChart chart = creator.createChart( context );
    String jsonChart = creator.chartToJson(chart);
    creator.sendResponse(jsonChart);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    processRequest(request, response);
}

}