오류 변수는 안티 패턴 또는 좋은 디자인입니까?


44

실행을 중단해서는 안되는 몇 가지 가능한 오류를 처리하기 위해 error클라이언트가 확인하고 예외를 throw하는 데 사용할 수 있는 변수가 있습니다. 안티 패턴입니까? 이것을 처리하는 더 좋은 방법이 있습니까? 이에 대한 예제는 PHP의 mysqli API를 볼 수 있습니다 . 가시성 문제 (접근 자, 공용 및 개인 범위, 클래스 또는 전역 변수)가 올바르게 처리되었다고 가정합니다.


6
이것은 try/ catch존재하는 것입니다. 또한, 당신은 넣을 수 있습니다 try/ catch(문제의 큰 분리 가능)을 처리하기위한보다 적절한 위치에 훨씬 더 위로 스택.
jpmc26

명심해야 할 사항 : 예외 기반 처리를 사용하고 예외가 발생하면 사용자에게 너무 많은 정보를 표시하고 싶지 않습니다. Elmah 또는 Raygun.io와 같은 오류 처리기를 사용하여 가로 채서 일반 오류 메시지를 사용자에게 표시하십시오. 사용자에게 스택 추적 또는 특정 오류 메시지를 표시하지 마십시오. 악용 될 수있는 앱의 내부 작업에 대한 정보가 공개되기 때문입니다.
Nzall

4
@Nate 조언은 사용자가 완전히 신뢰할 수없는 보안에 중요한 응용 프로그램에만 적용됩니다. 모호한 오류 메시지 자체는 안티 패턴입니다. 따라서 사용자의 명시적인 동의없이 네트워크를 통해 오류 보고서를 보내는 것입니다.
piedar

3
@piedar 나는 이것이 더 자유롭게 논의 될 수있는 별도의 질문을 만들었다 : programmers.stackexchange.com/questions/245255/…
Nzall

26
API 디자인의 일반적인 원칙은 PHP가하는 일을 항상 본 다음 정확히 반대의 일을하는 것입니다.
Philipp

답변:


65

언어가 본질적으로 예외를 지원하는 경우 예외를 처리하는 것이 좋으며 클라이언트는 예외가 발생하지 않도록 예외를 잡을 수 있습니다. 실제로 코드의 클라이언트는 예외를 예상하고 반환 값을 확인하지 않기 때문에 많은 버그가 발생합니다.

선택 사항이있는 경우 예외를 사용하면 몇 가지 장점이 있습니다.

메시지

예외에는 개발자가 디버깅에 사용하거나 원하는 경우 사용자에게 표시 할 수있는 사용자가 읽을 수있는 오류 메시지가 포함됩니다. 소비 코드가 예외를 처리 할 수없는 경우, 항상 예외를 로그 할 수 있으므로 개발자는 다른 모든 추적에서 중지 값을 파악하고 테이블에 맵핑하여 실제 예외.

리턴 값을 사용하면 추가 정보를 쉽게 제공 할 수 없습니다. 일부 언어는 마지막 오류 메시지를 얻기 위해 메소드 호출을 지원하므로이 문제는 약간 완화되지만 호출자는 추가 호출을해야하고 때로는이 정보를 전달하는 '특수 오브젝트'에 액세스해야합니다.

예외 메시지의 경우 가능한 많은 컨텍스트를 제공합니다.

사용자 프로필에서 참조 된 "bar"사용자에 대해 "foo"라는 이름의 정책을 검색 할 수 없습니다.

이것을 리턴 코드 -85와 비교하십시오. 어느 것이 더 좋은가요?

콜 스택

예외에는 일반적으로 코드를 더 빠르고 빠르게 디버그하는 데 도움이되는 자세한 호출 스택이 있으며 원하는 경우 호출 코드로 기록 할 수도 있습니다. 이를 통해 개발자는 일반적으로 문제를 정확하게 파악할 수 있으므로 매우 강력합니다. 다시 한 번,이 값을 반환 값 (예 : -85, 101, 0 등)이있는 로그 파일과 비교하십시오.

빠른 바이어스 접근 실패

실패한 곳에서 메소드를 호출하면 예외가 발생합니다. 호출 코드는 예외를 명시 적으로 억제해야합니다. 그렇지 않으면 실패합니다. 개발 및 테스트 중 (및 프로덕션에서도) 코드가 빠르게 실패하여 개발자가이를 수정해야하기 때문에 이것이 실제로 놀라운 것으로 나타났습니다. 반환 값의 경우 반환 값 확인이 누락되면 오류가 자동으로 무시되고 버그가 예상치 못한 곳에 나타나며 대개 디버깅 및 수정 비용이 훨씬 높습니다.

예외 랩핑 및 랩핑 해제

예외는 다른 예외에 싸서 필요한 경우 래핑 해제 할 수 있습니다. 예를 들어, ArgumentNullException호출 코드 UnableToRetrievePolicyException에서 해당 작업이 실패했기 때문에 코드에서 호출 코드를 감쌀 수있는 코드를 던질 수 있습니다 . 사용자에게 위에 제공된 예제와 유사한 메시지가 표시 될 수 있지만 일부 진단 코드는 예외를 풀고 ArgumentNullException문제가 발생한 것을 발견 할 수 있습니다 . 이는 소비자 코드의 코딩 오류임을 의미합니다. 그러면 개발자가 코드를 수정할 수 있도록 경고가 발생할 수 있습니다. 이러한 고급 시나리오는 반환 값으로 구현하기가 쉽지 않습니다.

코드의 단순성

이것은 설명하기가 조금 어렵지만 반환 값과 예외를 모두 사용 하여이 코딩을 통해 배웠습니다. 반환 값을 사용하여 작성된 코드는 일반적으로 호출 한 다음 반환 값이 무엇인지에 대한 일련의 검사를 수행합니다. 어떤 경우에는 다른 메소드를 호출하여 해당 메소드의 리턴 값에 대한 일련의 검사를 수행하게됩니다. 예외를 제외하면 예외 처리는 대부분의 경우가 아니라면 훨씬 간단합니다. try / catch / finally 블록이 있으며 런타임은 정리를 위해 finally 블록에서 코드를 실행하기 위해 최선을 다합니다. 중첩 된 try / catch / finally 블록조차도 중첩 된 if / else 및 여러 메서드의 관련 반환 값보다 추적 및 유지 관리가 상대적으로 쉽습니다.

결론

사용중인 플랫폼에서 예외 (예 : Java 또는 .NET)를 지원하는 경우 예외를 발생시키는 지침이 있으며 클라이언트가 예상 할 것이므로 예외를 발생시키는 것 외에 다른 방법은 없다고 가정해야합니다 그래서. 라이브러리를 사용하는 경우 예외가 발생하기를 기대하기 때문에 반환 값을 확인하지 않아도됩니다. 이러한 플랫폼의 세계입니다.

그러나 C ++ 인 경우 큰 코드베이스가 이미 리턴 코드와 함께 존재하고 많은 개발자가 예외가 아닌 값을 반환하도록 조정되어 있기 때문에 결정하기가 약간 더 어려울 것입니다 (예 : Windows는 HRESULT를 사용합니다) . 또한 많은 응용 프로그램에서 성능 문제가 될 수도 있습니다.


5
Windows는 공용 API에서 C 호환성을 유지하기 위해 C ++ 함수에서 HRESULT 값을 반환합니다 (그리고 경계를 넘어 예외를 마샬링하려고 시도하는 데 어려움을 겪기 시작합니다). 응용 프로그램을 작성하는 경우 운영 체제 모델을 맹목적으로 따르지 마십시오.
코디 그레이

2
이것에 추가 할 유일한 것은 느슨한 결합에 대한 언급입니다. 예외는 가장 적절한 장소에서 많은 예기치 않은 상황을 처리 할 수있게합니다. 예를 들어, 웹 앱에서는 웹 앱을 중단시키지 않고 코드가 준비되지 않은 예외 에 대해 500을 반환하려고 합니다. 따라서 코드 상단 (또는 프레임 워크)에서 모두 잡을 필요가 있습니다. 데스크탑 GUI에도 비슷한 상황이 있습니다. 그러나 현재 처리중인 프로세스에 적합한 방식으로 다양한 실패 상황을 처리하기 위해 코드의 다양한 위치에 일반 핸들러를 적게 배치 할 수도 있습니다.
jpmc26

2
@TrentonMaki C ++ 생성자의 오류에 대해 이야기하는 경우 가장 좋은 대답은 parashift.com/c++-faq-lite/ctors-can-throw.html 입니다. 요컨대, 예외를 던지 되, 잠재적 인 누출을 먼저 청소해야합니다. 생성자에서 바로 던지는 것이 나쁜 다른 언어는 알지 못합니다. 필자가 생각하는 대부분의 API 사용자는 오류 코드를 확인하는 대신 예외를 잡는 것을 선호합니다.
오우거 시편 33

3
"이러한 고급 시나리오는 반환 값으로 구현하기가 쉽지 않습니다." 물론 넌 할 수있어! ErrorStateReturnVariable수퍼 클래스를 생성하기 만하면되고, 그 속성 중 하나는 InnerErrorState(의 인스턴스 임 ErrorStateReturnVariable) 서브 클래스를 구현하면 일련의 오류를 보여 주도록 설정할 수 있습니다. : p
Brian S

5
언어가 예외를 지원한다고해서 만병 통치약이되는 것은 아닙니다. 예외는 숨겨진 실행 경로를 도입하므로 그 영향을 올바르게 제어해야합니다. try / catch는 쉽게 추가 할 수 있지만 복구 권한을 얻는 것은 어렵습니다 .
Matthieu M.

21

오류 변수는 예외를 사용할 수없는 C와 같은 언어의 유물입니다. 오늘날, C 프로그램 (또는 예외 처리없이 유사한 언어)에서 잠재적으로 사용되는 라이브러리를 작성할 때를 제외하고는이를 피해야합니다.

물론 "경고"로 더 잘 분류 될 수있는 유형의 오류가있는 경우 (= 라이브러리가 유효한 결과를 제공 할 수 있고 호출자가 중요하지 않다고 생각하면 경고를 무시할 수 있음) 양식의 상태 표시기 예외가있는 언어에서도 변수의 의미를 이해할 수 있습니다. 그러나 조심하십시오. 라이브러리 호출자는 그러한 경고를 무시해서는 안되는 경우에도 무시하는 경향이 있습니다. 따라서 그러한 구조를 lib에 도입하기 전에 두 번 생각하십시오.


1
설명 주셔서 감사합니다! 귀하의 답변 + Omer Iqbal이 내 질문에 답변했습니다.
Mikayla Maki 2016

"경고"를 처리하는 또 다른 방법은 기본적으로 예외를 발생시키고 예외 발생을 막기위한 일종의 선택적 플래그를 사용하는 것입니다.
Cyanfish 2016 년

1
@Cyanfish : 그렇습니다. 그러나 특히 라이브러리를 만들 때 그러한 것을 과도하게 디자인하지 않도록주의해야합니다. 더 나은 제공 간단한 및 이상 2, 3보다 경고 메커니즘을 작동합니다.
Doc Brown

실패, 알 수 없거나 복구 할 수없는 시나리오 ( 예외 상황) 가 발생한 경우에만 예외를 처리해야합니다 . 예외를 만들 때 성능에 영향을 미칠 것으로 예상됩니다.
Gusdor

@Gusdor : 절대적으로, 일반적인 "경고"가 기본적으로 예외를 발생시키지 않아야하는 이유입니다. 그러나 이것은 또한 추상화 수준에 약간 의존합니다. 때로는 서비스 나 라이브러리가 비정상적인 이벤트를 호출자의 관점에서 예외로 취급 할지를 결정할 수없는 경우가 있습니다. 개인적으로 그러한 상황에서 나는 경고 표시기 (예외 없음)를 설정하기 위해 lib를 선호합니다. 호출자가 그 플래그를 테스트하고 적절하다고 생각되면 예외를 던지도록하십시오. 그것이 제가 " 라이브러리에 하나의 경고 메커니즘 을 제공하는 것이 더 낫습니다"라고 썼을 때 제가 생각했던 것 입니다.
Doc Brown

20

오류를 알리는 방법에는 여러 가지가 있습니다.

  • 확인할 오류 변수 : C , Go , ...
  • 예외 : Java , C # , ...
  • "조건"처리기 : Lisp (만?), ...
  • 다형성 리턴 : Haskell , ML , Rust , ...

오류 변수의 문제점은 확인을 잊어 버리기 쉽다는 것입니다.

예외의 문제는 숨겨진 실행 경로를 생성한다는 것입니다. try / catch를 작성하기는 쉽지만 catch 절에서 적절한 복구를 수행하는 것은 실제로 어렵습니다 (유형 시스템 / 컴파일러의 지원 없음).

조건 핸들러의 문제점은 잘 작성되지 않는다는 것입니다. 동적 코드 실행 (가상 함수)이있는 경우 어떤 조건을 처리해야하는지 예측할 수 없습니다. 또한 동일한 조건을 여러 지점에서 발생시킬 수 있으면 매번 균일 한 솔루션을 적용 할 수 있다는 말이없고 빠르게 혼란스러워집니다.

Either a b하스켈의 다형성 리턴 은 지금까지 내가 가장 좋아하는 솔루션입니다.

  • 명시 적 : 숨겨진 실행 경로 없음
  • 명시 적 : 함수 유형 서명으로 완전히 문서화 됨 (놀람 없음)
  • 무시하기 어려움 : 원하는 결과를 얻으려면 패턴 일치를 설정하고 오류 사례를 처리해야합니다.

유일한 문제는 잠재적으로 과도한 점검으로 이어질 수 있다는 것입니다. 그것들을 사용하는 언어는 그것을 사용하는 함수의 호출을 연결하는 관용구를 가지고 있지만 여전히 약간의 타이핑 / 정리가 필요할 수 있습니다. Haskell에서 이것은 모나드 일 것입니다 . 그러나 이것은 소리보다 훨씬 두껍습니다 . 철도 지향 프로그래밍을 참조하십시오 .


1
좋은 대답입니다! 오류를 처리하는 방법 목록을 얻을 수 있기를 바랍니다.
Mikayla Maki 2016

아아! "오류 변수의 문제는 확인하는 것을 잊어 버리기 쉽다는 것"입니다. 예외와 관련된 문제는 그것을 잊어 버리는 것이 쉽다는 것입니다. 그런 다음 응용 프로그램이 충돌합니다. 상사는 문제를 해결하기 위해 비용을 지불하고 싶지 않지만 고객은 충돌로 인해 좌절하기 때문에 앱 사용을 중단합니다. 오류 코드가 리턴되고 무시 된 경우 프로그램 실행에 영향을 미치지 않은 무언가 때문입니다. 내가 사람들이 오류 코드를 무시하는 것을 본 유일한 시간은 그다지 중요하지 않은 때입니다. 체인의 상위 코드는 문제가 있음을 알고 있습니다.
덩크

1
@ 덩크 : 내 대답이 예외를 사과한다고 생각하지 않습니다. 그러나 작성하는 코드 유형에 따라 달라질 수 있습니다. 저의 개인적인 업무 경험은 자동 데이터 손상이 나쁘고 감지되지 않고 오류 가 발생 했을 때 실패 하는 시스템을 선호하는 경향이 있으며 제가 작업하는 데이터는 고객에게 가치가 있습니다 (물론 긴급한 수정도 의미합니다).
Matthieu M.

자동 데이터 손상 문제는 아닙니다. 앱을 이해하고 작업의 성공 여부를 언제 어디서 확인해야하는지 아는 문제입니다. 대부분의 경우, 결정 및 처리가 지연 될 수 있습니다. 예외가 필요한 실패한 작업을 처리해야 할 시점을 알려주는 것은 다른 사람에게 달려서는 안됩니다. 그것은 내 응용 프로그램이며 관련 문제를 언제 어디서 처리 해야하는지 알고 있습니다. 사람들이 데이터를 손상시킬 수있는 앱을 작성하는 것은 정말 열악한 일입니다. 충돌하는 앱을 작성하는 것도 좋지 않습니다.
덩크

12

끔찍하다고 생각합니다. 현재 예외 대신 반환 값을 사용하는 Java 앱을 리팩토링하고 있습니다. Java로 작업하지 않을 수도 있지만 그럼에도 불구하고 이것이 적용됩니다.

다음과 같은 코드로 끝납니다.

String result = x.doActionA();
if (result != null) {
  throw new Exception(result);
}
result = x.doActionB();
if (result != null) {
  throw new Exception(result);
}

아니면 이거:

if (!x.doActionA()) {
  throw new Exception(x.getError());
}
if (!x.doActionB()) {
  throw new Exception(x.getError());
}

나는 오히려 액션이 스스로 예외를 던지기를 원하므로 다음과 같은 결과가 나옵니다.

x.doActionA();
x.doActionB();

try-catch로 랩핑하고 예외에서 메시지를 가져 오거나 이미 사라 졌을 수있는 항목을 삭제하는 경우와 같이 예외를 무시하도록 선택할 수 있습니다. 스택 추적이있는 경우 스택 추적도 유지합니다. 방법 자체도 쉬워졌습니다. 예외 자체를 처리하는 대신 잘못 된 것을 던집니다.

현재 (끔찍한) 코드 :

private String doActionA() {
  try {
    someOperationThatCanGoWrong1();
    someOperationThatCanGoWrong2();
    someOperationThatCanGoWrong3();
    return null;
  } catch(Exception e) {
    return "Something went wrong!";
  }
}

새롭고 개선 된 기능 :

private void doActionA() throws Exception {
  someOperationThatCanGoWrong1();
  someOperationThatCanGoWrong2();
  someOperationThatCanGoWrong3();
}

쓸모없는 "문제가 발생했습니다!"대신 Strack 추적이 유지되고 예외적으로 메시지를 사용할 수 있습니다.

물론 더 나은 오류 메시지를 제공 할 수 있습니다. 그러나이 게시물은 현재 작업중 인 현재 코드가 고통 스럽기 때문에 여기에 있습니다.


1
그러나 한 가지 문제점은 '새롭고 개선 된'예외가 처음 발생한 위치의 컨텍스트를 잃는 상황이 있습니다. 예를 들어 doActionA ()의 "현재 (끔찍한) 버전"에서 catch 절은 더 유용한 메시지를 제공하기 위해 둘러싸는 객체의 인스턴스 변수 및 기타 정보에 액세스 할 수 있습니다.
정보 :

1
그것은 사실이지만 현재 일어나지 않습니다. 또한 doActionA에서 예외를 포착하여 상태 메시지로 다른 예외로 래핑 할 수 있습니다. 그러면 여전히 스택 추적 유용한 메시지가 나타납니다. throw new Exception("Something went wrong with " + instanceVar, ex);
mrjink

귀하의 상황에서는 발생하지 않을 수도 있음에 동의합니다. 그러나 "항상"정보를 doActionA ()에 넣을 수는 없습니다. 왜? doActionA () 호출자는 포함해야 할 정보를 보유한 유일한 사람 일 수 있습니다.
정보 :

2
따라서 예외를 처리 할 때 호출자에게이를 포함 시키십시오. 그러나 원래 질문에도 동일하게 적용됩니다. 예외로 할 수없는 일을 할 수있는 것은 없으며 코드를 깨끗하게 만듭니다. 반환 된 부울 또는 오류 메시지보다 예외를 선호합니다.
mrjink

"someOperation"내에서 발생하는 예외를 동일한 방식으로 처리하고 정리할 수 있다고 일반적으로 잘못된 가정을합니다. 실제 상황은 각 작업에 대한 예외를 잡아서 처리해야한다는 것입니다. 따라서 예제와 같이 예외를 throw하지 않습니다. 또한 예외를 사용하면 중첩 된 try-catch 블록 또는 일련의 try-catch 블록이 만들어집니다. 코드를 쉽게 읽을 수 없게 만드는 경우가 많습니다. 나는 예외에 대해 설정되어 있지 않지만 특정 상황에 적합한 도구를 사용합니다. 단 하나의 도구는 예외입니다.
덩크

5

"발생할 수있는 몇 가지 오류를 처리하기 위해 실행을 중단해서는 안됩니다."

오류가 현재 함수의 실행을 중단해서는 안되지만 호출자에게 어떤 방식으로보고해야한다는 것을 의미한다면 실제로 언급되지 않은 몇 가지 옵션이 있습니다. 이 경우는 실제로 오류보다 경고입니다. Throw / Returning은 현재 기능을 종료하므로 옵션이 아닙니다. 단일 오류 메시지 매개 변수 또는 리턴은 이러한 오류 중 최대 하나만 발생하도록 허용합니다.

내가 사용한 두 가지 패턴은 다음과 같습니다.

  • 전달되거나 멤버 변수로 유지되는 오류 / 경고 콜렉션. 당신은 물건을 추가하고 처리를 계속합니다. 나는 개인적으로이 접근 방식이 마음에 들지 않아 발신자를 불쾌하게 만듭니다.

  • 오류 / 경고 처리기 객체를 전달하거나 멤버 변수로 설정합니다. 그리고 각 오류는 핸들러의 멤버 함수를 호출합니다. 이렇게하면 호출자가 종료되지 않은 이러한 오류로 수행 할 작업을 결정할 수 있습니다.

이 컬렉션 / 핸들러에 전달하는 것은 오류가 "올바로"처리 될 수 있도록 충분한 컨텍스트를 포함해야합니다. 문자열은 일반적으로 너무 작아서 예외의 일부 인스턴스를 전달하는 경우가 종종 있습니다. .

오류 처리기를 사용하는 일반 코드는 다음과 같습니다.

class MyFunClass {
  public interface ErrorHandler {
     void onError(Exception e);
     void onWarning(Exception e);
  }

  ErrorHandler eh;

  public void canFail(int i) {
     if(i==0) {
        if(eh!=null) eh.onWarning(new Exception("canFail shouldn't be called with i=0"));
     }
     if(i==1) {
        if(eh!=null) eh.onError(new Exception("canFail called with i=1 is fatal");
        throw new RuntimeException("canFail called with i=2");
     }
     if(i==2) {
        if(eh!=null) eh.onError(new Exception("canFail called with i=2 is an error, but not fatal"));
     }
  }
}

3
사용자가 오류가 아니라 경고를 원한다는 것을 알기 위해 +1. warnings이 문제에 대한 또 다른 패턴을 제공 하는 Python 패키지를 언급 할 가치가 있습니다.
James_pic

큰 답변 주셔서 감사합니다! 이것은 내가보고 싶었던 것보다 전통적인 try / catch가 충분하지 않은 오류 처리를위한 추가 패턴입니다.
Mikayla Maki 2016

전달 된 오류 콜백 객체는 특정 오류나 경고가 발생할 때 (기본 동작 일 수 있음) 예외를 발생시킬 수 있지만, 요청하는 수단을 사용하는 것도 유용 할 수 있습니다. 무언가를하기위한 호출 함수. 예를 들어, "handle parsing error"메소드는 호출자에게 구문 분석이 리턴 된 것으로 간주되는 값을 제공 할 수 있습니다.
supercat

5

다른 사람이 사용하는 패턴을 사용하는 한이 패턴 또는 해당 패턴을 사용하는 데 아무런 문제가없는 경우가 종종 있습니다. 에서는 오브젝티브 C의 개발은 매우 양호한 패턴이 호출되는 메소드가 NSError 객체를 증착 할 수있는 포인터를 전달하는 것이다. Java 또는 .NET 프로그래머가 첫 번째 iPhone 앱을 작성 하지 않는 한 프로그래밍 오류로 인해 예외가 발생하여 충돌이 발생 합니다. 그리고 이것은 꽤 잘 작동합니다.


4

질문에 이미 답변되어 있지만 도움을 드릴 수 없습니다.

예외가 모든 사용 사례에 대한 솔루션을 제공한다고 기대할 수는 없습니다. 누구 망치?

예를 들어, 메소드가 요청을 받고 전달 된 모든 필드의 유효성을 검사 할 책임이있는 경우 (예 : 첫 번째 필드 만이 아니라 예외)가 전부가 아닌 예외가있는 경우가 있습니다. 둘 이상의 필드에 대한 오류의 원인을 나타냅니다. 유효성 검사의 특성으로 인해 사용자가 더 이상 갈 수 없는지 여부도 표시 할 수 있어야합니다. 그 예는 강력한 암호가 아닙니다. 입력 한 비밀번호가 강력하지는 않지만 충분히 강력하다는 메시지를 사용자에게 표시 할 수 있습니다.

이러한 모든 유효성 검사는 유효성 검사 모듈 끝에서 예외로 throw 될 수 있지만 이름 이외의 오류 코드 일 수 있습니다.

따라서 교훈은 다음과 같습니다. 예외는 오류 코드와 마찬가지로 위치가 있습니다. 현명하게 선택하십시오.


나는 이것이 오히려 나쁜 디자인을 의미한다고 생각합니다. 인수를 취하는 메소드는 그 사이의 아무것도 처리 할 수 ​​있어야합니다. 예제 Validator에는 문제의 메소드 (또는 그 뒤에있는 객체)에 (인터페이스)가 주입되어 있어야합니다. 주입 Validator된 방법에 따라이 방법은 잘못된 암호로 진행됩니다. 주변 코드 WeakValidator는 사용자가 예를 들어 WeakPasswordException처음 시도한에 의해 던져진 후에 요청하면 시도 할 수 StrongValidator있습니다.
jhr

아, 그러나 나는 이것이 인터페이스 또는 유효성 검사기가 될 수 없다고 말하지 않았다. 나는 JSR303에 대해서도 언급하지 않았다. 그리고 당신 이주의 깊게 읽으면, 나는 약한 암호를 말하지 말고 오히려 강력하지 않다는 것을 확신했습니다. 암호가 약하면 흐름을 멈추고 사용자에게 더 강한 암호를 요구하는 이유가됩니다.
Alexandre Santos

그리고 중간 정도의 강력하지만 실제적이지 않은 암호로 무엇을 하시겠습니까? 흐름을 중단하고 입력 한 암호가 그리 강력하지 않다는 메시지를 사용자에게 표시합니다. 그러니 MiddlyStrongValidator무언가가 있습니다. 그리고 이것이 실제로 흐름을 방해하지 않는다면 Validator, 사용자가 여전히 암호를 입력하는 동안 흐름을 진행하기 전에 미리 호출해야합니다. 그러나 검증은 우선 문제의 방법의 일부가 아니 었습니다. :) 아마 맛의 문제 ...
jhr

@jhr 필자가 작성한 유효성 검사기에서 일반적으로 AggregateException(또는 유사한 ValidationException)을 만들고 각 유효성 검사 문제에 대한 특정 예외를 InnerExceptions에 넣습니다. 예를 들어 BadPasswordException"사용자 암호가 최소 길이 6보다 작습니다"또는 MandatoryFieldMissingException"이름이 사용자에게 제공되어야합니다"등일 수 있습니다. 이는 오류 코드와 동일하지 않습니다. 이러한 모든 메시지는 사용자가 이해할 수있는 방식으로 사용자에게 표시 될 수 있으며 NullReferenceException대신 a 가 발생하면 버그가 발생합니다.
Omer Iqbal

4

오류 코드가 예외보다 선호되는 사용 사례가 있습니다.

오류에도 불구하고 코드가 계속 될 수 있지만보고가 필요한 경우 예외가 플로우를 종료하므로 예외를 선택하는 것이 좋지 않습니다. 예를 들어, 데이터 파일을 읽는 중이고 터미널에 불량이 아닌 일부 데이터가 포함되어있는 것을 발견하면 파일의 나머지 부분을 읽고 오류없이 오류를보고하는 것이 좋습니다.

다른 답변은 예외 코드가 일반적으로 오류 코드보다 선호되는 이유를 다루었습니다.


경고를 기록해야 할 경우 경고하십시오. 그러나 "보고가 필요합니다"라는 오류가 발생하면 사용자에게보고해야한다는 가정하에 주변 코드가 반환 코드를 읽고 실제로보고하도록 보장 할 방법이 없습니다.
jhr

아니요, 발신자에게보고하는 것을 의미합니다. 예외와 마찬가지로 사용자가 오류에 대해 알아야하는지 여부를 결정하는 것은 호출자의 책임입니다.
Jack Aidley

1
@ jhr : 언제 어떤 보증이 있습니까? 수업 계약은 고객에게 특정 책임이 있음을 명시 할 수 있습니다. 고객이 계약을 준수하면 계약에 필요한 작업을 수행합니다. 그렇지 않으면 결과는 클라이언트 코드의 결함입니다. 클라이언트가 실수로 실수하지 않도록 보호하고 직렬화 방법으로 반환 된 형식을 제어 할 수있는 경우 "알 수없는 손상 가능성"플래그가 포함되어 있고 클라이언트가 AcknowledgePossibleCorruption메서드 를 호출하지 않고 데이터를 읽을 수 없도록 할 수 있습니다 . .
supercat

1
...하지만 문제에 대한 정보를 보유 할 객체 클래스를 갖는 것은 예외를 던지거나 실패 오류 코드를 반환하는 것보다 도움이 될 수 있습니다. 해당 정보를 적절한 방식으로 사용하는 것은 응용 프로그램에 달려 있습니다 (예 : "Foo"파일을로드 할 때 데이터가 신뢰할 수 없음을 사용자에게 알리고 저장시 새 이름을 선택하라는 메시지를 표시 함).
supercat

1
예외를 사용하는 경우 한 가지 보증이 있습니다. 예외를 잡지 않으면 UI에 대해 최악의 경우가 더 높습니다. 아무도 읽지 않는 리턴 코드를 사용하는 경우에는 그러한 보장이 없습니다. 사용하고 싶다면 API를 따르십시오. 동의한다! 그러나 불행히도 오류의 여지가 있습니다 ...
jhr

2

예외가 잘 맞지 않을 때 예외를 사용하지 않으면 아무런 문제가 없습니다.

코드 실행이 중단되지 않아야하는 경우 (예 : 컴파일하는 프로그램이나 프로세스에 대한 형태와 같은 여러 오류가있을 수 있습니다 사용자 입력에 작용), 내가 좋아하는 에러 변수의 오류를 수집하는 것을 발견 has_errors하고하는 것은 error_messages참으로 던지는 것보다 훨씬 더 우아한 디자인입니다 첫 번째 오류의 예외 사용자가 불필요하게 다시 제출하지 않고도 사용자 입력의 모든 오류를 찾을 수 있습니다.


흥미로운 질문이 있습니다. 내 질문에 대한 문제와 나의 이해는 명확하지 않은 용어라고 생각합니다. 당신이 설명하는 것은 예외는 아니지만 오류입니다. 우리는 이것을 무엇이라고해야합니까?
Mikayla Maki

1

일부 동적 프로그래밍 언어에서는 오류 값예외 처리를 모두 사용할 수 있습니다 . 오류 값처럼 확인할 수있는 일반 반환 값 대신 throw되지 않은 예외 객체를 반환하여 수행 되지만 확인되지 않은 경우 예외가 발생합니다.

Perl 6 에서는을 통해 수행되며 fail, no fatal;범위를 포함하는 경우 특수 던지지 않은 예외 Failure객체를 반환 합니다.

펄 5 당신이 사용할 수있는 콘텐츠를 :: 반환 당신이이 작업을 수행 할 수 있습니다 return FAIL.


-1

매우 구체적인 것이 없다면 유효성 검사를 위해 오류 변수를 갖는 것이 나쁜 생각이라고 생각합니다. 목적은 유효성 검사에 소요되는 시간을 절약하는 것입니다 (변수 값만 반환 할 수 있음)

그러나 무엇이든 변경하면 어쨌든 해당 값을 다시 계산해야합니다. 나는 멈추고 예외 던지기에 대해 더 말할 수 없다.

편집 : 나는 이것이 특정한 경우가 아니라 소프트웨어 패러다임의 문제라는 것을 깨닫지 못했습니다.

내 대답이 이해되는 특정 사례에 대한 요점을 더 명확하게 설명하겠습니다.

  1. 엔터티 개체 모음이 있습니다.
  2. 이 엔티티 객체와 작동하는 절차 스타일 웹 서비스가 있습니다.

두 가지 종류의 오류가 있습니다.

  1. 서비스 계층에서 처리시 발생하는 오류
  2. 엔터티 개체에 불일치가 있기 때문에 오류

서비스 계층에서는 Result 객체를 래퍼로 사용하는 것 외에는 오류 변수와 동등한 선택이 없습니다. http와 같은 프로토콜에서 서비스 호출을 통해 예외를 시뮬레이션하는 것은 가능하지만 확실히 좋은 일은 아닙니다. 나는 이런 종류의 오류에 대해 이야기하고 있지 않으며 이것이이 질문에서 묻는 종류의 오류라고 생각하지 않았습니다.

나는 두 번째 종류의 오류에 대해 생각하고있었습니다. 그리고 제 대답은이 두 번째 종류의 오류에 관한 것입니다. 실체 개체에는 선택의 여지가 있으며 그중 일부는

  • 유효성 검사 변수 사용
  • 필드가 setter에서 잘못 설정되면 즉시 예외가 발생합니다.

유효성 검사 변수를 사용하는 것은 각 엔터티 개체에 대해 단일 유효성 검사 방법을 사용하는 것과 같습니다. 특히, 사용자는 세터를 순전히 세터로 유지하거나 부작용이없는 방식으로 값을 설정하거나 (이것은 종종 좋은 방법 임) 각 세터에 유효성 검증을 통합 한 다음 결과를 유효성 검증 변수에 저장할 수 있습니다. 이것의 장점은 시간을 절약하고 유효성 검사 결과를 유효성 검사 변수에 캐시하여 사용자가 validation ()을 여러 번 호출 할 때 다중 유효성 검사를 수행 할 필요가 없다는 것입니다.

이 경우 가장 좋은 방법은 유효성 검사 오류를 캐시하기 위해 유효성 검사를 사용하지 않고도 단일 유효성 검사 방법을 사용하는 것입니다. 이것은 세터를 그냥 세터로 유지하는 데 도움이됩니다.


당신이 무슨 말을하는지 봅니다. 재미있는 접근법. 답변 세트의 일부인 것이 기쁩니다.
Mikayla Maki
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.