我想询问是否有不同的方法可以解决我所拥有的if-else样板情况。
每个if
和else if
都有string.contains
个比较。我使用的是Java 8.
有没有办法使用像x ? y : z
这样的东西来缩短它?或者也许是开关?
if (s.contains("ServerAdmin ")) {
data.setServerAdmin(s) // This happens everywhere
} else if (s.contains("DocumentRoot")) {
data.setDocumentRoot(s) // This happens everywhere
} else if (s.contains("ServerName")) {
data.setServerName(s) // This happens everywhere
} else if (s.contains("ErrorLog")) {
data.setErrorLog(s) // This happens everywhere
} else if (s.contains("CustomLog")) {
data.setCustomLog(s) // This happens everywhere
} else if (s.contains("<Directory")) {
data.setDirectory(s) // This happens everywhere
}
有没有更好的方法来解决这个问题?
〜提前感谢您的任何帮助。
答案 0 :(得分:6)
我能想到的Java最简单的解决方案是
Map<String, Consumer<String>> map = new LinkedHashMap<>();
map.put("ServerAdmin", data::setServerAdmin);
map.put("DocumentRoot", data::setDocumentRoot);
// ...
map.entrySet().stream()
.filter(entry -> s.contains(entry.getKey()))
.findFirst()
.ifPresent(entry -> entry.getValue().accept(s));
使用LinkedHashMap
保持比较顺序完整(如果相关),则只需搜索第一个contains
并应用相应的函数
我想我会在实践中静态地声明地图,并将数据类的实例作为参数来保存每次执行的地图创建。例如,如下所示。
private static final Map<String, BiConsumer<Data, String>> MAP = new LinkedHashMap<>();
static {
MAP.put("ServerAdmin", Data::setServerAdmin);
MAP.put("DocumentRoot", Data::setDocumentRoot);
// ...
}
public void foo(Data data, String s) {
MAP.entrySet().stream()
.filter(entry -> s.contains(entry.getKey()))
.findFirst()
.ifPresent(entry -> entry.getValue().accept(data, s));
}
答案 1 :(得分:2)
您可以创建一组魔术字符串及其相应的操作,然后迭代这些字符串,而不是复制大的if-elseif-else
块。
private static final Map<String, BiConsumer<YourObj, String>> configActions;
static {
Map<String, BiConsumer<YourObj, String>> tmp = new HashMap<>();
tmp.put("ServerAdmin", YourObj::setServerAdmin);
tmp.put("DocumentRoot", YourObj::setDocumentRoot);
// ...
configActions = Collections.unmodifiableMap(tmp);
}
public static YourObj load(List<String> directives) {
YourObj config = new YourObj();
directives.forEach(dir -> set(config, configActions, dir));
return config;
}
static <T> void set(T obj, Map<String, BiConsumer<T, String>> setters, String value) {
setters.entrySet().stream()
.filter(e -> value.contains(e.getKey()))
.findFirst()
.ifPresent(e -> e.getValue().accept(obj, value));
}
通常,查找带有contains()
的魔术字符串并不是一种非常强大的控制逻辑方法。如果字符串s
遵循某种格式以确保您的搜索不会导致误报,请考虑解析该格式并提取令牌。如果输入字符串不遵循任何严格格式,则您很有可能首先登陆错误的令牌。例如,如果您的配置存储在YAML,XML或JSON文件中,请分别使用YAML,XML或JSON解析器。
作为一个例子,考虑如果你有令牌会发生什么事情&#34; LogFile&#34;和&#34; LogFileEncoding&#34;。 contain()
&#34; LogFile&#34;,但只有一个是正确的匹配。
如果您使用的是Java 9或更高版本,则可以像这样初始化Map
:
private static final Map<String, BiConsumer<YourObj, String>> configActions = Map.of(
"ServerAdmin", YourObj::setServerAdmin,
"DocumentRoot", YourObj::setDocumentRoot,
// ...
);
如果您有十个以上的条目,则可以使用entry()
构建器:
private static final Map<String, BiConsumer<YourObj, String>> configActions = Map.ofEntries(
Map.entry("ServerAdmin", YourObj::setServerAdmin),
Map.entry("DocumentRoot", YourObj::setDocumentRoot),
// ...
);
这些methods were added用于解决缺少收集文字问题,并避免现有解决方法存在的问题。
答案 2 :(得分:1)
有点hacky。但如果你感觉好些,试试吧:
public class Data {
private static Map<String, BiConsumer<Data, String>> SETTER_MAP = new LinkedHashMap<>(); // Use LinkedHashMap if you want to check the contains in order
static {
SETTER_MAP.put("ServerAdmin ", Data::setServerAdmin);
... your list goes on...
}
public void String setData(String s) {
SETTER_MAP.entrySet().stream().filter(e -> s.contains(e.getKey())
.findFirst().ifPresent(e -> e.getValue().accept(this, s);
}
}
答案 3 :(得分:1)
可能为了突出Java-8如何简化Java编码,我采取了基于Command模式OO的方法解决这个问题:
SELECT *
FROM recuperacion.inventario a JOIN recuperacion.Salidas b
ON a.codigo = b.codigo
WHERE a.codigo='5ae2399f4fbd3';
使用所有这些样板代码:
public interface Command {
public void execute(Data data, String containerString);
}
public abstract class AbstractCommand implements Command {
protected String containeeString;
protected AbstractBoundAction(String containeeString) {
this.containeeString = containeeString;
}
protected abstract void action(Data data, String containerString);
public void execute(Data data, String containerString) {
if(containerString.contains(containeeString)) {
action(data,containerString);
}
}
}
public class ServerAdminCommand extends AbstractCommand {
public ServerAdminCommand() {
super("ServerAdmin ");
}
protected void action(Data data, String containerString) {
data.setServerAdmin(containerString);
}
}
public class DocumentRootCommand extends AbstractCommand {
public DocumentRootCommand() {
super("DocumentRoot");
}
protected void action(Data data, String containerString) {
data.setDocumentRoot(containerString);
}
}
// And so on for the remaining data method invocation per contained strings
答案 4 :(得分:0)
我建议在s
的任何内容中添加一个字段,并创建列出可能案例的enum
:包含ServerAdmin,包含DocumentRoot等。调用此enum
字段{ {1}} s
出于我们的目的:
messageType
它更短,但保持可读性和直观性。
更新:刚看到switch (s.messageType){
case SERVER_ADMIN: data.setServerAdmin(s); break;
case DOCUMENT_ROOT: data.setDocumentRoot(s); break;
...
}
是s
。除非由于其他原因使String
成为您自己设计的对象,否则将其包装起来只是为了添加s
(我不会这样做)可能并不值得。甚至延伸enum
),所以我建议坚持String
s。
答案 5 :(得分:-1)
你可以做得更多java-8-ish:
enum Operation implements Predicate<String> {
SERVER_ADMIN {
@Override
public boolean test(String s) {
return s.contains("ServerAdmin");
}
@Override
public void accept(Data data, String input) {
data.setServerAdmin(input);
}
};
public abstract void accept(Data data, String input);
}
EnumSet.allOf(Operation.class)
.stream()
.filter(x -> x.test(s))
.findAny()
.ifPresent(x -> x.accept(data, s));