重构 switch 中的多个情况

huangapple go评论164阅读模式
英文:

Refactor multiple cases from switch

问题

TestDTO testDTO = new TestDTO();

for (Object attribute : row.getAttributes()) {
switch (attribute) {
case "CATEGORY":
testDTO.setCategory((String) attribute);
break;
case "DESCRIPTION":
testDTO.setDescription((String) attribute);
break;
case "NOTE":
testDTO.setNote((String) attribute);
break;
case "FEATURES":
testDTO.setFeatures((String) attribute);
break;
case "INDICATOR":
testDTO.setIndicator((String) attribute);
break;
case "LABEL":
testDTO.setLabel((String) attribute);
break;
case "TYPE":
testDTO.setType((String) attribute);
break;
default:
}
}

如您所见,在上面的代码中,我们使用多个案例来设置数据。代码运行良好。

有没有减少设置这些数据的多个案例的方法?

在上面的代码中,问题在于可维护性。因为假设我们有30个字段,那么我们需要为每个字段写30个案例。

是否有其他实现相同功能的方式?

英文:
TestDTO testDTO = new TestDTO();
	
for (Object attribute : row.getAttributes()) {
	switch (attribute) {
	case "CATEGORY":
		testDTO.setCategory((String) attribute);
		break;
	case "DESCRIPTION":
		testDTO.setDescription((String) attribute);
		break;
	case "NOTE":
		testDTO.setNote((String) attribute);
		break;
	case "FEATURES":
		testDTO.setFeatures((String) attribute);
		break;
	case "INDICATOR":
		testDTO.setIndicator((String) attribute);
		break;
	case "LABEL":
		testDTO.setLabel((String) attribute);
		break;
	case "TYPE":
		testDTO.setType((String) attribute);
		break;
	default:

	}
}

As you can see in above code, we are using multiple case for setting data. Code is working fine.

Is there any way for reducing multiple cases for setting those data.

In the above code, the problem is maintainability. Because suppose if we have 30 fields, then we need to put 30 cases for that.

Is there any other way to achieve the same?

答案1

得分: 5

不重构的话,你不能真正帮助情况。此外,你将需要为每个字段添加特定的代码 - 这是显而易见的。

在抽象情况下,你可以实现工厂方法模式或策略模式,并为每种属性类型注册适当的处理程序,类似于:

Map<Object, BiConsumer<TestoDTO, Object>> handlers; // 然后你可以添加例如 handlers.put("TYPE", (d, a) -> d.setType(a))

然后只需迭代属性:

row.getAttributes().forEach(a -> handlers.get(attribute).accept(dto, a)); // 当然,你需要处理所有情况,比如NPE,没有键等

在对象映射的范围内,你可以使用一些现有工具,比如ObjectMapper或ModelMapper,因为这些工具很可能会直接解决你的问题。

最后,也是最不推荐的解决方案是使用反射,将属性映射到字段名称,提取setter... 不要这样做 重构 switch 中的多个情况 它很糟糕,不安全,难以编写和理解 - 会导致许多问题,你会后悔的,但因为这是一个选项,我提到了这一点。

英文:

Without refactoring you cannot do anything really helping the situation. Also you will need to add specific code for every field anyway - this is obvious

In abstract situation what you could do would be to implement factory or strategy pattern and e.g. register proper handlers for every type of attribute - something like

Map&lt;Object, BiConsumer&lt;TestoDTO, Object&gt;&gt; handlers; // then you can add for example handlers.put(&quot;TYPE&quot;, (d, a) -&gt; d.setType(a))

And just iterate over attributes

row.getAttributes().forEach(a -&gt; handlers.get(attribute).accept(dto, a)); // ofc you need to handle all situation like NPE, no key etc

In scope of mapping objects you could use some existing tool like ObjectMapper or ModelMapper because it's quite possible that these tools will resolve your issue out of the box

Last and least (:)) solution is to use some reflection, map attribute to field name, extract setter... Don't do this 重构 switch 中的多个情况 it's filthy, insecure, hard to write and understand - will cause many issues you will regret but because it's an option I'm mentioning this

答案2

得分: 1

你可以使用反射来像下面这样重构它:

TestDTO testDTO = new TestDTO();

for (Object attribute : row.getAttributes()) {

    Method method = testDTO.getClass().getMethod("set" + capitalize((String) attribute), String.class);
    method.invoke(testDTO, (String) attribute);
}

大写首字母的函数:

public String capitalize(String string) {

    return string.substring(0, 1).toUpperCase() + string.substring(1).toLowerCase();
}
英文:

You can use reflection to refactor it like below:

TestDTO testDTO = new TestDTO();

for (Object attribute : row.getAttributes()) {

    Method method = testDTO.getClass().getMethod(&quot;set&quot; + capitalize((String) attribute), String.class);
    method.invoke(testDTO, (String) attribute);
}

The capitalize func:

public String capitalize(String string) {

    return string.substring(0, 1).toUpperCase() + string.substring(1).toLowerCase();
}

答案3

得分: 1

为了构建一个健壮的解决方案,您还可以使用枚举类型和方法引用来创建关联,并方便地封装映射到单一类型中。此外,如何添加新字段是相当明显的:

enum DTOMap
{
	CATEGORY(TestDTO::setCategory),
	DESCRIPTION(TestDTO::setDescription); 
	
	private final BiConsumer<TestDTO, String> attributeConsumer;
	
	private DTOMap(BiConsumer<TestDTO, String> attributeConsumer) {
		this.attributeConsumer = attributeConsumer;
	}
	
	public static void execute(TestDTO testDTO, Object attribute) {
		String attributeAsString = (String) attribute;
		DTOMap.valueOf(attributeAsString.toUpperCase()).attributeConsumer.accept(testDTO, attributeAsString);
	}
}

通过这样,您的 switch 语句可以减少到一行代码:

for (Object attribute : row.getAttributes()) {
    DTOMap.execute(testDTO, attribute);
}
英文:

For a robust solution you can also build your association using enumerated types and method references, and conveniently encapsulate the map into a single type. Plus, it's pretty obvious how to add new fields:

enum DTOMap
{
	CATEGORY(TestDTO::setCategory),
	DESCRIPTION(TestDTO::setDescription); 
	
	private final BiConsumer&lt;TestDTO, String&gt; attributeConsumer;
	
	private DTOMap(BiConsumer&lt;TestDTO, String&gt; attributeConsumer) {
		this.attributeConsumer = attributeConsumer;
	}
	
	public static void execute(TestDTO testDTO, Object attribute) {
		String attributeAsString = (String) attribute;
		DTOMap.valueOf(attributeAsString.toUpperCase()).attributeConsumer.accept(testDTO, attributeAsString);
	}
}

With this your switch statement can be reduced to a single line:

for (Object attribute : row.getAttributes()) {
    DTOMap.execute(testDTO, attribute);
}

huangapple
  • 本文由 发表于 2020年7月31日 20:16:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/63191666.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定