我正在编写一些代码,当我觉得丑陋而不优雅的时候,我会感受到 urgh 的感觉,但是我无法立即看到避免它。< / p>
我有一个json对象,我来自第三方。我知道会发生什么,但我无法确定每个元素肯定会在那里,所以我需要检查它是否存在如下:
if (object.has(ELEMENT)) {
JsonObject element = object.get(ELEMENT);
}
问题是我有时必须深入到对象中,当我得到这么多嵌套ifs时,它开始变得难看。这是一个例子:
private boolean lineExists(JsonArray orders, Line lineItem) {
final String LINE_ITEMS = "lineItems";
final String ELEMENTS = "elements";
final String NOTE = "note";
boolean exists = false;
log.debug("Checking if line item already exists in order...");
// if there is no order payload then the order hasn't already been posted so there can't be a duplicate line item
if (orders != null) {
for (int i = 0; i < orders.size(); i++) {
JsonObject order = orders.get(i).getAsJsonObject();
if (order.has(LINE_ITEMS)) {
JsonObject lineItems = order.get(LINE_ITEMS).getAsJsonObject();
if (lineItems.has(ELEMENTS)) {
JsonArray elements = lineItems.get(ELEMENTS).getAsJsonArray();
for (int j = 0; j < elements.size(); j++) {
JsonObject existingLine = elements.get(j).getAsJsonObject();
if (existingLine.has(NOTE)) {
String note = existingLine.get(NOTE).getAsString();
// the note may change after this comparison so just check if the ID is contained in the
// note
if (note.contains(lineItem.getNote())) {
exists = true;
log.warn("Line item with note containing '{}' already exists in order.",
lineItem.getNote());
}
}
}
}
}
}
}
return exists;
}
我知道我可以将一些测试分成他们自己的方法,如下所示:
private boolean lineExistCheck(JsonObject order) {
final String LINE_ITEMS = "lineItems";
final String ELEMENTS = "elements";
return order.has(LINE_ITEMS) && order.get(LINE_ITEMS).getAsJsonObject().has(ELEMENTS);
}
我只是想知道是否有一种设计模式或思维方式可以帮助我在这种情况下编写更好的代码。
答案 0 :(得分:3)
您想了解single layer of abstraction principle。
从本质上讲,每个要求你缩进的东西都应该更好地进入自己的方法。因此,您不仅要将测试放在单独的方法中;你走得更远。
并注意:使用这样的决赛...并没有给你买任何东西。在我看来(但这是对风格的看法!)这只是浪费,使你的CPU燃烧周期没有充分的理由。更进一步;将这些局部常量转换为全局静态值可能更有意义。
另一件事(考虑到你正在研究一个JsonObject,这当然很棘手)......这段代码是一个很好的&#34;反例Tell Dont Ask。关键是:您的代码正在查询其他对象的内部状态;根据这个做出决定。从本质上讲,这是程序性编程。在真实的OO中,你不会询问某个对象获取某些信息然后做某事;不;你只需告诉该对象做任何需要的事情。但正如所说;这在这里真的没有用;鉴于你正在处理一个通用的JsonObject;而不是真正的&#34;具有已知且定义明确的界面的对象,您可以使用它来告诉&#34;它该做什么。
答案 1 :(得分:1)
如何创建一个包含JsonObject
/ getLineItems
方法的getElements
包装器,如果没有行项目或元素,则会返回一个空数组。在这种情况下,您可以编写代码而无需检查是否存在特定属性。
在C#中,我们会使用一个库来实现JsonObject
与您的域类see this small example之间的转换,以获得一个想法。我很确定某些Java库中存在类似的功能。
答案 2 :(得分:1)
Java 8流API 可以帮助整理那些嵌套循环。在不知道您使用哪个JSON
库并且不想知道我将您的结构翻译成嵌套列表和地图的情况下:
JsonArray {
JsonObject {
String => JsonObject { // LINE_ITEMS
String => JsonArray { // ELEMENTS
JsonArray {
JsonObject {
String => String // NOTE
}
}
}
}
}
相当于
List<
Map<String, // LINE_ITEMS
Map<String, // ELEMENTS
List<
Map<
String, String // NOTE
>
>
>
>
>
我没有进一步重构代码(比如将字符串常量变成最终的静态变量,这是非常可取的)只集中在这个问题上:删除嵌套循环:
private static boolean lineExists(Optional<List<Map<String, Map<String, List<Map<String, String>>>>>> ordersOrNull, Line lineItem) {
final String LINE_ITEMS = "lineItems";
final String ELEMENTS = "elements";
final String NOTE = "note";
MutableBoolean exists = new MutableBoolean(false);
// if there is no order payload then the order hasn't already been posted so there can't be a duplicate line item
ordersOrNull.ifPresent(orders -> {
orders.stream().filter(order -> order.containsKey(LINE_ITEMS))
.map(order -> order.get(LINE_ITEMS))
.filter(lineItems -> lineItems.containsKey(ELEMENTS))
.flatMap(lineItems -> lineItems.get(ELEMENTS).stream())
.filter(element -> element.containsKey(NOTE) && element.get(NOTE).contains(lineItem.getNote()))
.map(existingLine -> existingLine.get(NOTE))
.findAny()
.ifPresent(n -> {
System.out.println(String.format("Line item with note containing %s already exists in order.", lineItem.getNote()));
exists.set();
});
});
return exists.get();
}
我使用Optional
来删除空检查并明确表示可以发生null,我使用MutableBoolean
来设置结果值,因为lambdas中使用的局部变量必须是有效的最终值
如果你问apache commons-lang中有MutableBoolean
,但它在问题的上下文中并不重要 - 它只是一个实现细节。此代码段的唯一目的是演示流API如何完全消除对嵌套循环和条件语句的需求。
为了完整性,这里是我的示例实现:
public static class MutableBoolean {
boolean value;
public MutableBoolean(boolean initialValue) {
this.value = initialValue;
}
public void set() {
value = true;
}
public void reset() {
value = false;
}
public boolean get() {
return value;
}
}
一个非常基本的程序来测试上面的代码:
interface Line {
String getNote();
}
public static void main(String[] args) {
Line line1 = new Line() {
public String getNote() {
return "nothing";
}
};
Line line2 = new Line() {
public String getNote() {
return "something";
}
};
Map<String, String> existingLine = new HashMap<>();
existingLine.put("note", "something");
List<Map<String, String>> elements = new ArrayList<>();
elements.add(existingLine);
Map<String, List<Map<String, String>>> lineItems = new HashMap<>();
lineItems.put("elements", elements);
Map<String, Map<String, List<Map<String, String>>>> order = new HashMap<>();
order.put("lineItems", lineItems);
List<Map<String, Map<String, List<Map<String, String>>>>> orders = new ArrayList<>();
orders.add(order);
System.out.println(lineExists(Optional.of(orders), line1));
System.out.println(lineExists(Optional.of(orders), line2));
}
答案 3 :(得分:0)
绝对调查https://github.com/daveclayton/json-schema-validator
为json对象编写模式,并根据模式验证它们。您不希望为您生成的每个应用程序中的每个不同的json对象编写验证代码。
这是一个根据模式String验证消息String的简单示例(一旦您创建了模式,当然)。
public void validate(String schema, String msg) {
JsonNode schemaNode = JsonLoader.fromString(schema);
JsonNode msgNode = JsonLoader.fromString(msg);
JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
JsonSchema jsonSchema = factory.getJsonSchema(schemaNode);
ProcessingReport report = jsonSchema.validate(msgNode);
if (!report.isSuccess()) {
throw new RuntimeException(report.toString());
}
}
您可以使用该报告做更多事情,但这可以让您了解如何根据任何模式验证任何json消息应该是多么微不足道,就像使用xml一样。