首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >重构Java工厂方法

重构Java工厂方法
EN

Stack Overflow用户
提问于 2008-09-22 14:50:58
回答 16查看 6.8K关注 0票数 15

这段代码有一些非常不令人满意的地方:

代码语言:javascript
复制
/*
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语句并不满意。我曾经考虑过做一个字符串到命令的映射:

代码语言:javascript
复制
commandMap = new HashMap();
commandMap.put("START",StartCommand.class);
// ... etc.

..。然后使用反射来创建从Map中查找的适当类的实例。然而,虽然在概念上很优雅,但这涉及到大量的反射代码,无论谁继承了这段代码,都可能不会欣赏这些代码-尽管这些成本可能会被好处所抵消。将值硬编码到commandMap中的所有行几乎都和if块一样难闻。

如果工厂的构造函数能够扫描类路径中Command的子类,查询它们的字符串表示形式,并自动将它们添加到它的指令表中,那就更好了。

那么--我该如何进行重构呢?

我猜有些框架是免费提供给我这类东西的。让我们假设我无法将这些东西迁移到这样的框架中。

EN

回答 16

Stack Overflow用户

回答已采纳

发布于 2008-09-22 14:58:14

我认为你的字符串到命令的映射很好。您甚至可以将字符串命令名分解为构造函数(即StartCommand不应该知道它的命令是“START”吗?)如果可以这样做,命令对象的实例化就会简单得多:

代码语言:javascript
复制
Class c = commandMap.get(cmdName);
if (c != null)
    return c.newInstance();
else
    throw new IllegalArgumentException(cmdName + " is not as valid command");

另一种选择是创建所有命令的enum,并将其链接到类(假设所有命令对象都实现了CommandInterface):

代码语言:javascript
复制
public enum Command
{
    START(StartCommand.class),
    END(EndCommand.class);

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

因为枚举的toString就是它的名称,所以可以使用EnumSet来定位正确的对象并从内部获取类。

票数 12
EN

Stack Overflow用户

发布于 2008-09-22 15:42:51

下面的代码如何:

代码语言:javascript
复制
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)来创建命令对象。

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

票数 14
EN

Stack Overflow用户

发布于 2008-09-22 15:00:57

除了

代码语言:javascript
复制
cmd.subString(0,8).trim();

部分,这在我看来并不是很糟糕。您可以使用Map并使用反射,但是,根据您添加/更改命令的频率,这可能不会给您带来太多好处。

您可能应该记录为什么只需要前8个字符,或者可能更改协议,以便更容易找出该字符串的哪一部分是命令(例如,在命令关键字后面放一个像':‘或';’这样的标记)。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/115269

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档