英文:
Refactoring: removal of duplication in constructor
问题
我似乎缺乏足够的咖啡来清楚地看待以下问题。
想象一下,我有一个带有两个构造函数和多个字段的类。一个构造函数是无参构造函数,一个字段依赖于另一个字段。另一个构造函数为其字段之一接受一个注入值。示例:
public class Practice {
private final int n;
private final char c;
private final Map<String, String> m;
private final Set<String> s;
public Practice() {
this.n = 0;
this.c = 'a';
this.m = new HashMap<>();
this.s = m.keySet();
}
public Practice(Set<String> s) {
this.n = 0;
this.c = 'a';
this.m = new HashMap<>();
this.s = s;
}
}
我的问题:我如何消除两个构造函数之间的代码重复?
第一个失败的尝试:
public Practice() {
this(new HashMap<>(), new HashMap<>().keySet());
}
public Practice(Set<String> s) {
this(new HashMap<>(), s);
}
private Practice(int n, char c, Map<String, String> m, Set<String> s) {
this.n = 0;
this.c = 'a';
this.m = m;
this.s = s;
}
当然,这个尝试失败了,因为无参构造函数创建了两个单独的Map,而不是一个。
英文:
I seem to be lacking enough coffee to make me see the following problem clearly.
Imagine I have a class with two constructors and several fields. One constructor is a no-arg constructor and one field depends on another. The other constructor takes an injected value for one of its fields. Example:
public class Practice {
private final int n;
private final char c;
private final Map<String, String> m;
private final Set<String> s;
public Practice() {
this.n = 0;
this.c = 'a';
this.m = new HashMap<>();
this.s = m.keySet();
}
public Practice(Set<String> s) {
this.n = 0;
this.c = 'a';
this.m = new HashMap<>();
this.s = s;
}
}
My question: how do I remove the duplication of code between the two constructors?
First failed attempt:
public Practice() {
this(new HashMap<>(), new HashMap<>().keySet());
}
public Practice(Set<String> s) {
this(new HashMap<>(), s);
}
private Practice(int n, char c, Map<String, String> m, Set<String> s) {
this.n = 0;
this.c = 'a';
this.m = m;
this.s = s;
}
Of course, this fails because the no-args constructor creates two separate Maps instead of one.
答案1
得分: 3
在某些情况下,一个参数取决于另一个参数,您可以通过添加额外的构造函数来解决这个问题。在这种情况下 private Practice(Map<String,String> map)
public Practice() {
this(new HashMap<>());
}
public Practice(Set<String> s) {
this(new HashMap<>(), s);
}
private Practice(Map<String,String> map) {
this(map, map.keySet());
}
private Practice(Map<String,String> map, Set<String> s) {
this.n = 0;
this.c = 'a';
this.m = map;
this.s = s;
}
英文:
In cases where one argument depends on another argument you can solve the problem by adding an additional constructor. In this case private Practice(Map<String,String> map)
public Practice() {
this(new HashMap<>());
}
public Practice(Set<String> s) {
this(new HashMap<>(), s);
}
private Practice(Map<String,String> map) {
this(map, map.keySet());
}
private Practice(Map<String,String> map, Set<String> s) {
this.n = 0;
this.c = 'a';
this.m = map;
this.s = s;
}
答案2
得分: 3
你可以在定义成员变量的同时初始化它们。在构造函数中,你可以只初始化那些在初始化后可能会持有不同值的变量。
另外注意:由于 n
和 c
是基本数据类型,它们适合被声明为 static
,因为它们也被标记为 final
。但对于 m
来说,情况不同。
最后,没有必要消除代码中的每一个重复部分。关于这一点的规则各不相同,但通常我遵循三次重复原则,或者当重复的代码块很大时才进行重构。
英文:
You can just initialize variables as you define them as members. In your constructor you can stick to initializing strictly those variables that can hold a different value after initialization.
Also note: Since n
and c
are primitives they are suitable candidates to be static
, since they are also tagged final
. This does not hold true for m
.
public class Practice {
private static final int n = 0;
private static final char c = 'a';
private final Map<String, String> m = new HashMap<>();
private final Set<String> s;
public Practice() {
this.s = m.keySet();
}
public Practice(Set<String> s) {
this.s = s;
}
}
Lastly - it is not necessary to kill each and any duplication of code. The rules on this vary, though I usually follow the rule-of-three or when the duplicated chunks are large.
答案3
得分: 1
对于初始版本,您可以从默认构造函数传递一个 null
,然后在设置 s
时检查是否为 null
:
public class Practice {
private final int n;
private final char c;
private final Map<String, String> m;
private final Set<String> s;
public Practice() {
this(null);
}
public Practice(Set<String> s) {
this.n = 0;
this.c = 'a';
this.m = new HashMap<>();
this.s = null == s ? m.keySet() : s;
}
}
类似地,具有三个构造函数的版本可以进行更新:
public Practice() {
this(null);
}
public Practice(Set<String> s) {
this(0, 'a', new HashMap<>(), s); // 由于所有参数构造函数是私有的
}
private Practice(int n, char c, Map<String, String> m, Set<String> s) {
this.n = n;
this.c = c;
this.m = m;
this.s = null == s ? m.keySet() : s;
}
英文:
For the initial version, you could pass a null
from the default constructor and then check for null
when setting s
:
public class Practice {
private final int n;
private final char c;
private final Map<String, String> m;
private final Set<String> s;
public Practice() {
this(null);
}
public Practice(Set<String> s) {
this.n = 0;
this.c = 'a';
this.m = new HashMap<>();
this.s = null == s ? m.keySet() : s;
}
}
Similarly, the version with 3 constructors can be updated:
public Practice() {
this(null);
}
public Practice(Set<String> s) {
this(0, 'a', new HashMap<>(), s); // as all args constructor is private
}
private Practice(int n, char c, Map<String, String> m, Set<String> s) {
this.n = n;
this.c = c;
this.m = m;
this.s = null == s ? m.keySet() : s;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论