Java의 생성자에서 개인 최종 멤버 변수를 선언하고 설정하는 올바른 방법은 무엇입니까?
-
20-09-2019 - |
문제
생성자에서 멤버 변수를 설정하는 방법에는 여러 가지가 있습니다.실제로 최종 멤버 변수, 특히 도우미 클래스에 의해 항목이 로드되는 맵을 올바르게 설정하는 방법에 대해 토론하고 있습니다.
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;
}
}
내 생각에는 생성자에서 이러한 변수를 설정하는 방법을 사용하는 것이 더 나은 방법입니다.따라서 기본 클래스는 다음과 같습니다.
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 클래스에서 새 지도를 생성합니까? 두 경우 모두 Helper는 실제 로딩을 수행하고 구체적인 명령이 포함된 지도에 대한 Base의 참조는 비공개적이고 최종적입니다.
제가 고려하고 있는 옵션 외에 이 작업을 수행할 수 있는 다른 더 우아한 방법이 있을까요?
해결책
당신이 그것을 흡수 할 수 없다면, 당신은 타사 API를 사용할 필요가 없다면, 당신은 다음을 사용할 수 있습니다.
java.util.Collections.unmodifiableMap(Map m)
이를 수행하는 가장 일반적인 방법은 다음과 같습니다.
public class Base { private final Map availableCommands; public Base(){ availableCommands=new HashMap(); // or any other kind of map that you wish to load availableCommands = Helper.loadCommands(availableCommands); } }
다른 팁
첫 번째 코드 스 니펫에 따라 도우미 클래스가 맵을 만드는 것이 전적으로 합리적입니다. 너 ~이다 생성자에서 변수를 설정 - 문제를 볼 수 없습니다.
Yawn이 말했듯이,지도를 불변으로 만드는 것은 여기에서 좋은 터치가 될 것이지만, 그 외에는 첫 번째 스 니펫에서 코드를 사용합니다.
(실제 생활에서 이것은 실제로 정적 인 인스턴스 변수가되어야한다고 생각합니까?)
그러한지도가 불변이되기를 원한다면 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();
나와 같은 빌더 패턴 사용을 고려해 보셨습니까? 효과적인 Java 2nd ed.?
한 곳에 모든 맵 구성 논리를 캡처 할 수 있습니다 (따라서 유지할 별도의 클래스 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 b = new Base.Builder().addCommand("c1", c1).addCommand("c2", c2).build();
Upshot은 클라이언트 클래스가지도를 작성해야한다는 것을 알 필요가 없으며 본질적으로 1 줄로 모두 빌드 할 수 있다는 것입니다. 단점은 생성자가 현재 비공개이기 때문에베이스를 확장 할 수 없다는 것입니다 (아마도 원할 수도 있습니다.
편집 : build ()에 멍청이가 있었는 edit2 : base.builder.addcommand에 넣는 대신 실수로 Add를 호출
그냥하지 그래
private final Map<String, Command> availableCommands = Helper.loadCommands();
?
개인적으로 도우미 클래스의 이름을 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는 동기화되어야하며 각 조회는 더 비쌀 것입니다. 지도가 읽기 전용이라고 가정합니다. 그렇지 않으면 다중 스레드 환경에서 어쨌든 동기화 된 맵이 필요합니다.
이중 중괄호 초기화를 사용할 수도 있습니다. 더 깔끔하다고 생각하는지 여부는 취향의 문제일 수 있지만 적어도 모든 초기화 코드를 한 곳에 보관할 수 있다는 이점이 있습니다.
public class Base {
public final Map< String, Command > availableCommands;
public Base() {
availableCommands = Collections.unmodifiableMap( new HashMap() {
{
put( "A", new CommandA() );
put( "B", new CommandB() );
}
} );
}
}