当前位置:  开发笔记 > 编程语言 > 正文

重构Java工厂方法

如何解决《重构Java工厂方法》经验,为你挑选了2个好方法。

这段代码有些令人非常不满意:

/*
Given a command string in which the first 8 characters are the command name
padded on the right with whitespace, construct the appropriate kind of 
Command object.
*/
public class CommandFactory {
     public Command getCommand(String cmd) {
         cmdName = cmd.subString(0,8).trim();

         if(cmdName.equals("START")) {
             return new StartCommand(cmd);
         }
         if(cmdName.equals("END")) {
             return new EndCommand(cmd);
         }
         // ... more commands in more if blocks here
         // else it's a bad command.
         return new InvalidCommand(cmd);
     }
}

我对多个出口点没有悔改 - 结构清晰.但我对一系列几乎相同的if语句并不满意.我考虑过将一个字符串映射到命令:

commandMap = new HashMap();
commandMap.put("START",StartCommand.class);
// ... etc.

...然后使用Reflection从Map中查找相应类的实例.然而,虽然在概念上优雅,但这涉及相当多的反射代码,任何继承此代码的人都可能不会欣赏 - 尽管这些成本可能会被优势所抵消.将命令值硬编码到commandMap中的所有行几乎都与if块一样糟糕.

更好的是,如果工厂的构造函数可以扫描类路径以查找Command的子类,查询它们的String表示,并自动将它们添加到其库中.

那么 - 我该如何重构呢?

我想有些框架可以免费提供给我这种东西.让我们假设我无法将这些东西迁移到这样的框架中.



1> Roel Spilker..:

以下代码怎么样:

public enum CommandFactory {
    START {
        @Override
        Command create(String cmd) {
            return new StartCommand(cmd);
        }
    },
    END {
        @Override
        Command create(String cmd) {
            return new EndCommand(cmd);
        }
    };

    abstract Command create(String cmd);

    public static Command getCommand(String cmd) {
        String cmdName = cmd.substring(0, 8).trim();

        CommandFactory factory;
        try {
            factory = valueOf(cmdName);
        }
        catch (IllegalArgumentException e) {
            return new InvalidCommand(cmd);
        }
        return factory.create(cmd);
    }
}

valueOf(String)枚举是用来找到正确的工厂方法.如果工厂不存在,它会抛出一个IllegalArgumentException.我们可以将其用作创建InvalidCommand对象的信号.

一个额外的好处是,如果你也可以使方法create(String cmd)公开,如果你也可以这样构建一个Command对象编译时间检查可用于其余的代码.然后,您可以使用CommandFactory.START.create(String cmd)来创建Command对象.

最后一个好处是您可以在Javadoc文档中轻松创建所有可用命令的列表.



2> davetron5000..:

我认为你的命令字符串映射是好的.您甚至可以将字符串命令名称分解为构造函数(即,不应该让StartCommand知道它的命令是"START"?)如果您可以这样做,那么实例化命令对象要简单得多:

Class c = commandMap.get(cmdName);
if (c != null)
    return c.newInstance();
else
    throw new IllegalArgumentException(cmdName + " is not as valid command");

另一种选择是enum使用指向类的链接创建所有命令(假设所有命令对象都实现CommandInterface):

public enum Command
{
    START(StartCommand.class),
    END(EndCommand.class);

    private Class mappedClass;
    private Command(Class c) { mappedClass = c; }
    public CommandInterface getInstance()
    {
        return mappedClass.newInstance();
    }
}

因为枚举的toString是它的名字,你可以EnumSet用来定位正确的对象并从内部获取类.

推荐阅读
k78283381
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有