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();
    }
}

しかし、現在、最終修飾子を維持できず、コンパイラエラーが発生します(最終変数を設定できません)

これを行う別の方法は次のとおりです。

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>();かです。私の主な質問は、機能の一部がこのヘルパーの静的メソッドからのものであり、実際のマップにエントリをロードする方法として、これを行うための推奨される方法があるかどうかです。

BaseクラスまたはHelperクラスで新しいマップを作成しますか?どちらの場合も、ヘルパーが実際の読み込みを行い、具体的なコマンドを保持しているマップへのBaseの参照は非公開で最終的なものになります。

私が検討しているオプション以外に、これを行うためのよりエレガントな方法はおそらく他にありますか?

4

7 に答える 7

3

最初のコードスニペットのように、ヘルパークラスがマップを作成することは私には完全に合理的であるように思われます。コンストラクターで変数を設定しています-問題がわかりません。

あくびが言うように、マップを不変にするのはここではいい感じですが、それ以外は最初のスニペットのコードを使用します。

(ちなみに、実際には、これは静的なものではなく、インスタンス変数である必要があると思いますか?)

于 2009-11-17T15:25:22.667 に答える
2

このようなマップを不変にしたい場合は、GoogleCollectionAPIをご覧ください。リンクされたドキュメントを引用するには:

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 に答える
2

Effective Java2ndedのようなBuilderパターンの使用を検討しましたか。?

すべてのマップ構築ロジックを1か所でキャプチャできます(したがって、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() に間抜けがありました編集2:Base.Builder.addCommandに置く代わりに誤ってaddを呼び出しました

于 2009-11-17T15:36:40.860 に答える
2
  1. サードパーティのAPIを使用する必要がない不変の場合は、次を使用できます。java.util.Collections.unmodifiableMap(Map m)

  2. これを行う最も一般的な方法は次のとおりです。

パブリッククラスベース{
プライベートファイナルマップavailableCommands;
public Base(){
  availableCommands = new HashMap(); //またはロードしたい他の種類のマップ
  availableCommands = Helper.loadCommands(availableCommands);  
 }
}
于 2009-11-17T17:05:36.420 に答える
1

やってみませんか

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

于 2009-11-17T20:20:55.470 に答える
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() {
        ...
    }
}

ロードが1回だけ行われるように同期されます。getCommandも同期する必要があり、各ルックアップのコストが高くなるため、getCommandはありません。マップは読み取り専用であると想定しています。そうでない場合は、マルチスレッド環境ではとにかくsynchronizedMapが必要になります。

于 2009-11-17T15:38:50.830 に答える
0

二重中括弧の初期化を使用することもできますが、それがよりクリーンであると思うかどうかはおそらく好みの問題ですが、少なくともすべての初期化コードを1か所にまとめるという利点があります。

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 に答える