如何在使用基本Throwable的派生类型时替代Java中的if-else代码块?

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

How to replace if-else blocks in Java when working with derived types from the basic Throwable?

问题

以下是您提供的代码的翻译部分:

例如有一个代码根据类型**Throwable**的子类型将填充对象并将其返回给调用点

@Component
@Slf4j
public class GlobalErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Map<String, Object> map = new LinkedHashMap<>();
        Throwable error = super.getError(request);
        HttpStatus errorStatus;
        String messageDetail = null;

        if (error instanceof UserAlreadyExistsException) {

            Throwable cause = error.getCause();

            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }

            errorStatus = HttpStatus.NOT_ACCEPTABLE;
            log.error("error - {}, status - {}, detailMessage - {}",
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        }

        if (error instanceof UserNotFoundException ||
                error instanceof ResponseStatusException || error instanceof PasswordInHashNotFoundException) {

            Throwable cause = error.getCause();

            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }

            errorStatus = HttpStatus.NOT_FOUND;
            log.error("error - {}, status - {}, detailMessage - {}",
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        } else if (error instanceof BadSqlGrammarException) {

            Throwable cause = error.getCause();

            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }

            errorStatus = HttpStatus.NOT_ACCEPTABLE;
            log.error("BadSqlGrammar error: error - {}, status - {}, detailMessage - {}",
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        } else {

            Throwable cause = error.getCause();

            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }

            errorStatus = HttpStatus.INTERNAL_SERVER_ERROR;
            log.error("Not defined error: error - {}, status - {}, detailMessage - {}",
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        }
        map.put("status", errorStatus.value());
        map.put("message", error.getMessage());
        map.put("detailMessage", messageDetail);
        return map;
    }
}
我创建了一个模型如下

Map<Throwable, Supplier<String>> handler = new LinkedHashMap<>();

PasswordInHashException passwordInHashException =
        new PasswordInHashException("passwordInHashException", new Throwable());

PasswordInHashNotFoundException passwordInHashNotFoundException =
        new PasswordInHashNotFoundException("passwordInHashException");


handler.put(passwordInHashException, () -> {
    System.out.println("passwordInHashException");
    return "passwordInHashException";
});

handler.put(passwordInHashNotFoundException, () -> {
    System.out.println("passwordInHashNotFoundException");
    return "passwordInHashNotFoundException";
});
然而困难在于如何使用instanceof运算符强制在Map中检查键

由于有许多异常类型并且这种方法将增长但我希望能够在不更改此方法的情况下扩展处理新异常的能力更好的是使用Spring的自动组件初始化功能然后只需实现一个新类型它将自动添加到Map中并且如果需要根据发生的异常返回填充对象并执行程序化操作例如写入日志)。

Map<String, Supplier<String>> handler = new HashMap<>();

PasswordInHashException passwordInHashException =
        new PasswordInHashException("passwordInHashException", new Throwable());

handler.put("PasswordInHashException", () -> {
    System.out.println("passwordInHashException");
    return "passwordInHashException";
});

Throwable key = new PasswordInHashException("passwordInHashException", new Throwable());
String simpleName = key.getClass().getSimpleName();

handler.get(simpleName).get();

请分享您的想法。

英文:

For example, there is a code that, depending on the subtype of the type Throwable, will fill the object with data and return it to the call point.

@Component
@Slf4j
public class GlobalErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map&lt;String, Object&gt; getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Map&lt;String, Object&gt; map = new LinkedHashMap&lt;&gt;();
        Throwable error = super.getError(request);
        HttpStatus errorStatus;
        String messageDetail = null;

        if (error instanceof UserAlreadyExistsException) {

            Throwable cause = error.getCause();

            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }

            errorStatus = HttpStatus.NOT_ACCEPTABLE;
            log.error(&quot;error - {}, status - {}, detailMessage - {}&quot;,
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        }

        if (error instanceof UserNotFoundException ||
                error instanceof ResponseStatusException || error instanceof PasswordInHashNotFoundException) {

            Throwable cause = error.getCause();

            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }

            errorStatus = HttpStatus.NOT_FOUND;
            log.error(&quot;error - {}, status - {}, detailMessage - {}&quot;,
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        }else if (error instanceof BadSqlGrammarException) {

            Throwable cause = error.getCause();

            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }

            errorStatus = HttpStatus.NOT_ACCEPTABLE;
            log.error(&quot;BadSqlGrammar error: error - {}, status - {}, detailMessage - {}&quot;,
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        } else {

            Throwable cause = error.getCause();

            if (cause != null) {
                messageDetail = error.getCause().getMessage();
            }

            errorStatus = HttpStatus.INTERNAL_SERVER_ERROR;
            log.error(&quot;Not defined error: error - {}, status - {}, detailMessage - {}&quot;,
                    error.getMessage(),
                    errorStatus,
                    messageDetail);
        }
        map.put(&quot;status&quot;, errorStatus.value());
        map.put(&quot;message&quot;, error.getMessage());
        map.put(&quot;detailMessage&quot;, messageDetail);
        return map;
    }
}

A made a model like this


        Map&lt;Throwable, Supplier&lt;String&gt;&gt; handler = new LinkedHashMap&lt;&gt;();

        PasswordInHashException passwordInHashException =
                new PasswordInHashException(&quot;passwordInHashException&quot;, new Throwable());

        PasswordInHashNotFoundException passwordInHashNotFoundException =
                new PasswordInHashNotFoundException(&quot;passwordInHashException&quot;);


        handler.put(passwordInHashException, () -&gt; {
            System.out.println(&quot;passwordInHashException&quot;);
            return &quot;passwordInHashException&quot;;
        });

        handler.put(passwordInHashNotFoundException, () -&gt; {
            System.out.println(&quot;passwordInHashNotFoundException&quot;);
            return &quot;passwordInHashNotFoundException&quot;;
        });

However, the difficulty lies in how to force the key to be checked in Map using the instanceof operator.

Since there are a lot of Exceptions types and this method will grow, but I would like to make it so that without changing this method, expand the ability to process new Exceptions. And it is even better to use Spring's capabilities for automatic initialization of components, then it is enough to implement a new type that will be automatically added to the Map and, if necessary, depending on the exceptions that occurred, the filled object was returned and a programmed action was performed (for example, writing to the log).

 Map&lt;String, Supplier&lt;String&gt;&gt; handler = new HashMap&lt;&gt;();

        PasswordInHashException passwordInHashException =
                new PasswordInHashException(&quot;passwordInHashException&quot;, new Throwable());

  handler.put(&quot;PasswordInHashException&quot;, () -&gt; {
            System.out.println(&quot;passwordInHashException&quot;);
            return &quot;passwordInHashException&quot;;
        });

 Throwable key =  new PasswordInHashException(&quot;passwordInHashException&quot;, new Throwable());
        String simpleName = key.getClass().getSimpleName();

         handler.get(simpleName).get();

Please share your ideas.

答案1

得分: 0

以下是您的代码的中文翻译:

有几种选择

# 1. 多态

异常自己知道在这里如何行为

    记录 StatusAndMessage(HttpStatus errorStatus, String message, String messageDetail) {}

    接口 SelfHandling {
        StatusAndMessage handle()
    }
    
    @Component
    @Slf4j
    public class GlobalErrorAttributes extends DefaultErrorAttributes {

        @Override
        public Map&lt;String, Object&gt; getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
            Throwable error = super.getError(request);
            StatusAndMessage statusAndMessage;

            if (error instanceof SelfHandling selfHandling) {
                statusAndMessage = selfHandling.statusAndMessage()
            } else {
                statusAndMessage = new StatusAndMessage(
                    HttpStatus.INTERNAL_SERVER_ERROR,
                    error.getMessage(),
                    error.getCause() != null ? error.getCause().getMessage() : null;
                )
            }
            log.error("未定义的错误:错误 - {},状态 - {},详细消息 - {}",
                    statusAndMessage.message(),
                    statusAndMessage.errorStatus(),
                    statusAndMessage. messageDetail());
            map.put("status", statusAndMessage.errorStatus().value());
            map.put("message", statusAndMessage.message());
            map.put("detailMessage", statusAndMessage. messageDetail());
            return map;
        }
    }

 - 不利之处对于第三方异常无效因此您必须包装自己的异常或选择不同的方法
 - 优势如果定义新异常则无需更改此方法

# 2. 双重分发

 - 不利之处丑陋因此没有代码示例
 - 优势可以根据上下文变化行为例如根据请求异常可能需要不同的状态

# 3. 责任链

	// 如果无法处理Throwable,则实现将返回Optional.empty()
	interface ExceptionToResponseMapper extends Function&lt;Throwable, Optional&lt;StatusAndMessage&gt;&gt; {
	}

	@Component
	@Slf4j
	public class GlobalErrorAttributes extends DefaultErrorAttributes {

		private final List&lt;ExceptionToResponseMapper&gt; mappers;

		// mappers的顺序很重要
		public GlobalErrorAttributes(List&lt;ExceptionToResponseMapper&gt; mappers) {
			this.mappers = List.copyOf(mappers);
		}

		@Override
		public Map&lt;String, Object&gt; getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {

			Throwable error = super.getError(request);
			StatusAndMessage statusAndMessage = mappers.stream()
				.map(mapper -&gt; mapper.apply(error))
				.flatMap(Optional::stream)
				.findFirst()
				.orElseGet(() -&gt; new StatusAndMessage(
						HttpStatus.INTERNAL_SERVER_ERROR,
						error.getMessage(),
						error.getCause() != null ? error.getCause().getMessage() : null
				));
			log.error("未定义的错误:错误 - {},状态 - {},详细消息 - {}",
					statusAndMessage.message(),
					statusAndMessage.errorStatus(),
					statusAndMessage. messageDetail());
			map.put("status", statusAndMessage.errorStatus().value());
			map.put("message", statusAndMessage.message());
			map.put("detailMessage", statusAndMessage. messageDetail());
			return map;
		}
    }

 - 不利之处在引入新异常时容易忘记编写额外的处理程序
 - 优势在第三方异常上轻松工作

希望这有助于您理解代码的中文翻译。如果您有任何其他问题,请随时提出。

英文:

There are a few options :

1. Polymorphism

The exceptions know how to behave here themselves

record StatusAndMessage(HttpStatus errorStatus, String message, String messageDetail) {}
interface SelfHandling {
StatusAndMessage handle()
}
@Component
@Slf4j
public class GlobalErrorAttributes extends DefaultErrorAttributes {
@Override
public Map&lt;String, Object&gt; getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
Throwable error = super.getError(request);
StatusAndMessage statusAndMessage;
if (error instanceof SelfHandling selfHandling) {
statusAndMessage = selfHandling.statusAndMessage()
} else {
statusAndMessage = new StatusAndMessage(
HttpStatus.INTERNAL_SERVER_ERROR,
error.getMessage(),
error.getCause() != null ? error.getCause().getMessage() : null;
)
}
log.error(&quot;Not defined error: error - {}, status - {}, detailMessage - {}&quot;,
statusAndMessage.message(),
statusAndMessage.errorStatus(),
statusAndMessage. messageDetail());
map.put(&quot;status&quot;, statusAndMessage.errorStatus().value());
map.put(&quot;message&quot;, statusAndMessage.message());
map.put(&quot;detailMessage&quot;, statusAndMessage. messageDetail());
return map;
}
}
  • Downside : This doesn't work for 3rd party exceptions. So you have to either wrap those in your own, or choose a different approach.
  • Upside : this method doesn't need to change if you define new Exceptions

2. Double dispatch

  • Downside : ugly (so no code example)
  • Upside : You can vary behavior
    per context. If exceptions may need a different status depending on
    the request for instance.

3. Chain of Responsibility

// implementations will return Optional.empty() if they can&#39;t handle the Throwable
interface ExceptionToResponseMapper extends Function&lt;Throwable, Optional&lt;StatusAndMessage&gt;&gt; {
}
@Component
@Slf4j
public class GlobalErrorAttributes extends DefaultErrorAttributes {
private final List&lt;ExceptionToResponseMapper&gt; mappers;
// order of the mappers matters
public GlobalErrorAttributes(List&lt;ExceptionToResponseMapper&gt; mappers) {
this.mappers = List.copyOf(mappers);
}
@Override
public Map&lt;String, Object&gt; getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
Throwable error = super.getError(request);
StatusAndMessage statusAndMessage = mappers.stream()
.map(mapper -&gt; mapper.apply(error))
.flatMap(Optional::stream)
.findFirst()
.orElseGet(() -&gt; new StatusAndMessage(
HttpStatus.INTERNAL_SERVER_ERROR,
error.getMessage(),
error.getCause() != null ? error.getCause().getMessage() : null
));
log.error(&quot;Not defined error: error - {}, status - {}, detailMessage - {}&quot;,
statusAndMessage.message(),
statusAndMessage.errorStatus(),
statusAndMessage. messageDetail());
map.put(&quot;status&quot;, statusAndMessage.errorStatus().value());
map.put(&quot;message&quot;, statusAndMessage.message());
map.put(&quot;detailMessage&quot;, statusAndMessage. messageDetail());
return map;
}
}
  • Downside : easy to forget writing extra handlers when introducing a new Exception
  • Upside : works easily on 3rd party exceptions

huangapple
  • 本文由 发表于 2023年6月9日 04:09:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76435381.html
匿名

发表评论

匿名网友

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

确定