3

有多种方法可以从构造函数中设置成员变量。我实际上是在讨论如何正确设置最终成员变量,特别是一个由助手类加载条目的映射。

public class Base {
    private final Map<String, Command> availableCommands;
    public Base() {
        availableCommands = Helper.loadCommands();  
    }
}

在上面的示例中,帮助程序类如下所示:

public class Helper {
    public static Map<String, Command> loadCommands() {
        Map<String, Command> commands = new HashMap<String, Command>();
        commands.put("A", new CommandA());
        commands.put("B", new CommandB());
        commands.put("C", new CommandC());

        return commands;
    }
}

我的想法是,使用方法在构造函数中设置这样的变量是更好的做法。所以 Base 类看起来像这样:

public class Base {
    private final Map<String, Command> availableCommands;
    public Base() {
        this.setCommands();  
    }
    private void setCommands() {
        this.availableCommands = Helper.loadCommands();
    }
}

但是现在我无法维护 final 修饰符并出现编译器错误(无法设置最终变量)

另一种方法是:

public class Base {
    private final Map<String, Command> availableCommands = new HashMap<String, Command>();
    public Base() {
        this.setCommands();
    }
    private void setCommands() {
        Helper.loadCommands(availableCommands);
    }
}

但在这种情况下,Helper 类中的方法将更改为:

public static void loadCommands(Map<String, Command> commands) {
    commands.put("A", new CommandA());
    commands.put("B", new CommandB());
    commands.put("C", new CommandC());
}

所以不同的是我在哪里创建一个新地图new HashMap<String, Command>();我的主要问题是,是否有推荐的方法来执行此操作,因为部分功能来自此 Helper 的静态方法,作为加载带有条目的实际地图的一种方式?

我是在 Base 类还是 Helper 类中创建新地图?在这两种情况下,Helper 将执行实际加载,并且 Base 对包含具体命令的地图的引用将是私有的和最终的。

除了我正在考虑的选项之外,是否还有其他更优雅的方法可以做到这一点?

4

7 回答 7

3

根据您的第一个代码片段,帮助类创建地图对我来说似乎是完全合理的。您在构造函数设置变量 - 我看不到问题。

正如打哈欠所说,使地图不可变在这里会是一个不错的选择,但除此之外,我只使用第一个片段中的代码。

(我假设在现实生活中这真的需要是一个实例变量,而不是一个静态变量,顺便说一下?)

于 2009-11-17T15:25:22.667 回答
2
  1. 如果您希望它不可变,则不需要使用 3rd 方 API,您可以使用:java.util.Collections.unmodifiableMap(Map m)

  2. 最常见的方法是:

公共类基{
私有最终地图可用命令;
公共基础(){
  可用命令=新哈希映射();// 或您希望加载的任何其他类型的地图
  availableCommands = Helper.loadCommands(availableCommands);  
 }
}
于 2009-11-17T17:05:36.420 回答
2

您是否考虑过使用像Effective Java 2nd ed 中的 Builder 模式。?

您可以在一个地方捕获所有地图构造逻辑(因此您无需维护 2 个单独的类)。基地看起来像这样:

public class Base {

    private final Map<String, Command> commands;

    private Base(Builder b) {
        commands = b.commands;
    }

    public static class Builder() {

        private final Map<String, Command> commands;

        public Builder() {
            commands = new HashMap<String, Command>();
        }

        public Builder addCommand(String name, Command c) {
            commands.put(name, c);
            return this;
        }

        public Base build() {
            return new Base(this);
        }
    }
}

Base 的客户现在可以这样工作:

Base b = new Base.Builder().addCommand("c1", c1).addCommand("c2", c2).build();

结果是客户端类不需要知道他们需要构建一个地图,你基本上可以用 1 行来构建它。缺点是 Base 不能扩展,因为构造函数现在是私有的(也许你想要,也许你不想要)。

编辑:在 build() 中有一个错误,我传递了命令而不是我最初想要的 EDIT2:错误地调用 add 而不是放入 Base.Builder.addCommand

于 2009-11-17T15:36:40.860 回答
2

如果您希望此类地图不可变,请查看Google Collection API。引用链接的文档:

static final ImmutableMap<String, Integer> WORD_TO_INT =
       new ImmutableMap.Builder<String, Integer>()
           .put("one", 1)
           .put("two", 2)
           .put("three", 3)
           .build();
于 2009-11-17T15:34:31.410 回答
1

你为什么不做

private final Map<String, Command> availableCommands = Helper.loadCommands();  

?

于 2009-11-17T20:20:55.470 回答
0

您也可以使用双括号初始化,尽管您是否认为它更干净可能是个人喜好问题,但至少具有将所有初始化代码放在一个位置的好处:

public class Base {
    public final Map< String, Command > availableCommands;

    public Base() {
        availableCommands = Collections.unmodifiableMap( new HashMap() {
            {
                put( "A", new CommandA() );
                put( "B", new CommandB() );
            }
        } );
    }
}
于 2009-11-17T17:13:24.500 回答
0

我个人会将 Helper 类重命名为 CommandHolder:

public class CommandHolder {
    private static Map<String, Command> availableCommands;
    private static CommandHolder instance;

    private CommandHolder{}
    public static synchronized Map<String, Command> getCommandMap() {
        if (instance == null) {
            instance = new CommandHolder();
            instance.load();
        }
        return availableCommands
    }
    private void load() {
        ...
    }
}

同步以确保加载只发生一次。没有 getCommand 因为那个也必须同步,并且每次查找都会更昂贵。我假设地图是只读的,否则在多线程环境中无论如何你都需要一个同步地图。

于 2009-11-17T15:38:50.830 回答