Java 또는 C#에서의 예외 관리 베스트 프랙티스
지원서의 예외를 어떻게 처리해야 할지 막막해요.
예외와 관련된 문제가 1) 원격 서비스를 통해 데이터에 액세스하거나 2) JSON 개체를 역직렬화하는 데 기인하는 경우입니다.유감스럽게도 이러한 작업(네트워크 연결 끊기, JSON 개체 형식이 잘못되어 내 통제 범위를 벗어남)에 대해서는 성공을 보장할 수 없습니다.
그 결과, 예외가 발생했을 경우는, 기능내에서 검출해, FALSE 를 발신자에게 반환합니다.내 논리는 발신자가 정말로 신경 쓰는 것은 작업이 왜 성공하지 못했는지가 아니라 성공했는지 여부라는 것이다.
다음은 일반적인 메서드의 샘플 코드(JAVA)입니다.
public boolean doSomething(Object p_somthingToDoOn)
{
boolean result = false;
try{
// if dirty object then clean
doactualStuffOnObject(p_jsonObject);
//assume success (no exception thrown)
result = true;
}
catch(Exception Ex)
{
//don't care about exceptions
Ex.printStackTrace();
}
return result;
}
이 접근방식은 괜찮다고 생각합니다만, 예외를 관리하기 위한 베스트 프랙티스가 무엇인지 알고 싶습니다(콜 스택에 예외를 버블링해야 합니까?).
주요 질문의 요약:
- 예외를 포착할 뿐이지 버블이 발생하지 않거나 시스템에 정식으로 통지(로그 또는 사용자에게 통지)해도 됩니까?
- 테스트/캐치 블록이 필요한 예외에 대해 어떤 베스트 프랙티스가 있습니까?
폴로업/편집
피드백을 주셔서 감사합니다.온라인에서 예외 관리에 관한 훌륭한 소스를 찾았습니다.
- 예외 처리의 베스트 프랙티스| 오라일리 미디어
- 의 예외 처리 베스트프랙티스그물
- 베스트 프랙티스: 예외 관리(이 문서는 archive.org의 복사본을 가리키고 있습니다)
- 예외 처리 안티패턴
예외 관리는 상황에 따라 다른 것 중 하나인 것 같습니다.그러나 가장 중요한 것은 시스템 내에서 예외를 관리하는 방법을 일관되게 유지해야 한다는 것입니다.
또한 과도한 시도/캐치 또는 예외에 대한 존중 없이 코드 로트에 주의하십시오(예외로 시스템에 경고하는 경우, 그 밖에 무엇을 경고해야 합니까?).
또한 m3rLinEz의 예쁜 코멘트입니다.
나는 대부분의 통화자가 운영의 성공 여부에 대해서만 관심을 갖는다는 Anders Hejlsberg와 당신의 의견에 동의하는 경향이 있다.
이 코멘트에서 예외를 다룰 때 고려해야 할 몇 가지 질문이 나타납니다.
- 이 예외가 발생하는 요점은 무엇입니까?
- 어떻게 처리하는 게 말이 되나요?
- 발신자는 정말로 예외에 대해 신경을 쓰고 있습니까, 아니면 콜이 성공했는지에 대해서만 신경을 쓰고 있습니까?
- 발신자에게 잠재적인 예외를 관리하도록 강요하는 것은 적절한가?
- 당신은 그 언어의 어리석음을 존중하고 있습니까?
- 부울과 같은 성공 플래그를 반환해야 합니까?부울(또는 int)을 반환하는 것은 Java(Java에서는 예외 처리만 가능)보다 C 마인드입니다.
- 언어와 관련된 오류 관리 구조를 따르십시오. : )!
예외를 잡아서 에러 코드로 바꾸려고 하는 것은 이상하다고 생각합니다.Java와 C# 모두 디폴트인 예외보다 발신자가 에러 코드를 선호하는 이유는 무엇입니까?
문의 사항:
- 실제로 처리할 수 있는 예외만 포착해야 합니다.대부분의 경우 예외를 잡는 것은 옳은 일이 아닙니다.몇 가지 예외(예: 스레드 간의 로깅 및 마샬링 예외)가 있지만, 이러한 경우에도 일반적으로 예외를 다시 적용해야 합니다.
- 코드에는 try/catch 문장이 많이 포함되어 있지 않아야 합니다.다시 말씀드리지만, 이 아이디어는 처리할 수 있는 예외만 잡는 것입니다.처리되지 않은 예외를 최종 사용자에게 유용한 것으로 변환하기 위해 최상위 예외 핸들러를 포함할 수 있지만 그렇지 않은 경우 가능한 모든 위치에서 모든 예외를 탐지하려고 해서는 안 됩니다.
이것은 어플리케이션과 상황에 따라 다릅니다.라이브러리 구성요소를 구성하는 경우 예외는 구성요소와 상황에 맞게 래핑해야 하지만 버블해야 합니다.예를 들어 XML 데이터베이스를 구축하고 파일 시스템을 사용하여 데이터를 저장하고 파일 시스템 권한을 사용하여 데이터를 보호한다고 가정합니다.파일을 버블링하고 싶지 않을 것입니다.IOAccess 구현이 누출되므로 예외가 거부되었습니다.대신 예외를 랩하고 AccessDenied 오류를 발생시킵니다.이것은 특히 컴포넌트를 서드파티에 배포하는 경우에 해당됩니다.
예를 들어 예외를 삼켜도 괜찮다는 듯이.시스템에 따라 다릅니다.사용하시는 어플리케이션이 실패 사례를 처리할 수 있고 실패 이유를 사용자에게 알려도 아무런 이점이 없다면 계속 진행하십시오. 단, 실패를 로그에 기록할 것을 강력히 권장합니다.문제의 트러블 슈팅을 지원하기 위해 전화하는 것은 항상 불만족스러운 일이었습니다.또한 문제를 해결하려고 전화하는 사람이 예외를 삼키고 있습니다(또는 내부 예외를 설정하지 않고 교체하고 새 예외를 던지는 경우).
일반적으로 다음 규칙을 사용합니다.
- 컴포넌트나 라이브러리에서 예외를 발견하는 것은 그 예외에 따라 처리하거나 작업을 수행할 경우뿐입니다.또는 예외적으로 추가 컨텍스트 정보를 제공하려는 경우.
- 응용 프로그램 진입점 또는 가능한 한 높은 레벨에서 일반적인 트라이캐치를 사용합니다.예외가 발생하면 로그로 기록하고 실패합니다.이상적으로는 예외가 여기에 있어서는 안 된다.
냄새는 다음 코드입니다.
try
{
//do something
}
catch(Exception)
{
throw;
}
이와 같은 코드는 의미가 없으므로 포함하면 안 됩니다.
저는 이 주제에 대한 또 다른 좋은 소식통을 추천하고 싶습니다.C#과 Java의 발명가 Anders Hejlsberg와 James Gosling의 Java Checked Exception에 대한 인터뷰입니다.
페이지 하부에 훌륭한 자원도 있습니다.
나는 대부분의 통화자가 운영의 성공 여부에 대해서만 관심을 갖는다는 Anders Hejlsberg와 당신의 의견에 동의하는 경향이 있다.
Bill Venners:체크된 예외에 관한 scalability 및 versioning에 관한 우려를 언급했습니다.그 두 가지 문제가 무엇을 의미하는지 설명해 주시겠습니까?
앤더스 헤일스버그:우선 버전 관리부터 시작합시다.왜냐하면 이 문제는 여기서 쉽게 볼 수 있기 때문입니다.예를 들어 예외 A, B 및 C를 발생시킨다고 선언하는 메서드 foo를 만듭니다.foo 버전2에서는 많은 기능을 추가하고 싶기 때문에 foo가 예외 D를 발생시킬 수 있습니다.이 메서드의 throughs 절에 D를 추가하는 것은 큰 변화입니다.그 메서드의 기존 발신자는 그 예외를 처리하지 않을 것이 거의 확실하기 때문입니다.
새 버전의 throws 구에 새 예외를 추가하면 클라이언트 코드가 끊어집니다.인터페이스에 메서드를 추가하는 것과 같습니다.인터페이스를 퍼블리시 한 후에는 실질적으로 변경할 수 없습니다.인터페이스의 실장에는 다음 버전에서 추가하는 메서드가 포함되어 있을 수 있기 때문입니다.그 대신 새로운 인터페이스를 작성해야 합니다.예외와 마찬가지로 foo2라는 완전히 새로운 메서드를 생성하여 예외를 발생시키거나 새로운 foo에서 예외D를 포착하여 D를 A, B 또는 C로 변환해야 합니다.
Bill Venners: 하지만 당신은 그들의 코드를 해독하고 있지 않나요? 심지어 체크된 예외가 없는 언어에서도요.만약 새로운 버전의 foo가 고객이 취급에 대해 생각해야 할 새로운 예외를 발생시킨다면, 코드를 작성할 때 그 예외를 예상하지 못했다는 사실만으로 코드가 깨지는 것은 아닐까요?
안데르스 헤일스버그: 아니요, 왜냐하면 많은 경우 사람들은 신경 쓰지 않기 때문입니다.이러한 예외는 처리하지 않습니다.메시지 루프 주위에 하위 수준의 예외 핸들러가 있습니다.그 핸들러는, 무엇이 잘못되었는지를 나타내는 다이얼로그를 표시해, 속행합니다.프로그래머는 최종적으로 어디에서나 try를 작성하여 코드를 보호하므로 예외가 발생할 경우 올바르게 되돌릴 수 있지만 실제로는 예외 처리에 관심이 없습니다.
적어도 Java에서 구현되는 방식인 throws 절에서는 예외를 처리하도록 강제할 필요는 없지만 처리하지 않으면 통과할 수 있는 예외를 정확하게 인식하도록 강제할 수 있습니다.선언된 예외를 캐치하거나 자신의 throws 절에 넣어야 합니다.이 요건을 회피하기 위해 사람들은 우스꽝스러운 일을 한다.예를 들어, 모든 방법을 "throws Exception"으로 장식합니다.그러면 기능이 완전히 상실되고 프로그래머가 더 골칫덩어리를 쓰게 됩니다.그건 아무에게도 도움이 안 돼요.
EDIT: 변환에 대한 자세한 내용을 추가
체크된 예외는 일반적으로 논란의 여지가 있는 문제이며, 특히 Java에서는 (나중에 찬성하는 사람과 반대하는 사람의 예를 몇 가지 찾아보겠습니다.)
경험의 법칙으로서 예외 처리는 다음 가이드라인을 따르는 것으로 합니다.특정 순서는 없습니다.
- 유지보수를 위해 항상 예외를 기록하여 버그가 발견되기 시작할 때 로그가 버그가 시작된 위치를 가리킬 수 있도록 합니다. 떠나지 않다
printStackTrace()
예를 들어, 사용자 중 한 명이 최종적으로 스택트레이스 중 하나를 취득할 가능성이 높기 때문에 이 스택트레이스를 어떻게 해야 할지 전혀 알 수 없습니다. - 처리할 수 있는 예외를 포착하고, 그 예외만을 처리할 수 있습니다.그것들을 그냥 쌓아올리지 마세요.
- 클래스를으로 타입은 안 돼요.일반적으로 유형을 잡으면 안 함
Exception
다른 중요한 예외를 삼킬 가능성이 매우 높다. - Never (ever) catch s!!는 다음과 같은 의미입니다.를 로서 포착하지 않다
Error
는 후자의 서브클래스입니다.Error
「」, 「」, 「」, 「」, 「」OutOfMemory
JVM)
특정 케이스에 대해서는 메서드를 호출하는 클라이언트가 적절한 반환값을 수신하는지 확인합니다.뭔가 실패했을 경우 부울 반환 메서드가 false를 반환할 수 있지만 해당 메서드를 호출한 장소에서 처리할 수 있는지 확인하십시오.
처리할 수 있는 예외만 잡아야 합니다.예를 들어, 네트워크를 통한 읽기를 처리하고 있는데 연결 시간이 초과되어 예외가 발생하면 다시 시도할 수 있습니다.그러나 네트워크를 통해 읽고 있을 때 IndexOutOfBounds 예외가 발생하면 원인을 모르기 때문에 이 문제를 처리할 수 없습니다.false, -1 또는 null을 반환할 경우 특정 예외를 위해 반환해야 합니다.사용 중인 라이브러리에서 힙의 메모리가 부족할 때 네트워크에서 읽은 false를 반환하고 싶지 않습니다.
예외는 일반 프로그램 실행의 일부가 아닌 오류입니다.프로그램의 동작과 사용법(워드 프로세서와 하트 모니터 등)에 따라 예외가 발생했을 때 다른 작업을 수행할 수 있습니다.통상적인 실행의 일부로서 예외를 사용하는 코드로 작업한 적이 있습니다만, 이것은 확실히 코드 냄새입니다.
예.
try
{
sendMessage();
if(message == success)
{
doStuff();
}
else if(message == failed)
{
throw;
}
}
catch(Exception)
{
logAndRecover();
}
이 암호는 나를 토하게 한다.IMO 중요한 프로그램이 아니면 예외에서 복구하지 마십시오.만약 당신의 투척 예외가 있다면 나쁜 일이 일어나고 있는 것입니다.
위의 모든 것이 합리적인 것으로 보이며, 종종 당신의 직장에는 정책이 있을 수 있습니다.집에서는 예외의 했습니다.SystemException
및 (기호) »ApplicationException
(체크박스를 켜겠습니다).
는 동의했습니다.SystemException
는 복구가 불가능할 가능성이 높으며 위에서 한 번 처리됩니다. 자세한 내용을 하기 위해, 델의 「 」를 참조해 주세요.SystemException
되어 있습니다( 「」, 「」등).RepositoryException
,ServiceEception
등등.
ApplicationException
수 .InsufficientFundsException
클라이언트 코드로 처리해야 합니다.
구체적인 예를 들자면, 귀사의 구현에 대해 언급하기는 어렵지만, 반환 코드는 사용하지 않습니다. 유지 보수 문제이기 때문입니다.예외를 무시할 수도 있지만 이유를 결정하고 항상 이벤트와 스택 트레이스를 기록해야 합니다. 이 다른 가 없기 되어 있기 에, 이 방법은 캡슐화 이외에는 중복되지 않습니다.doactualStuffOnObject(p_jsonObject);
울!을 !!!!!!!!!!!
잠시 생각한 후 코드를 보면 단순히 예외를 부울로 재투입하는 것처럼 보입니다.메서드가 이 예외를 통과시키고(잡지 않아도 됨) 발신자 내에서 처리할 수 있습니다.그것이 중요한 장소이기 때문입니다.예외로 인해 발신자가 이 기능을 재시도할 경우, 발신자가 예외를 검출해야 합니다.
경우에 따라서는, 발생하고 있는 예외가 발신자에게 의미가 없는 경우가 있습니다(즉, 네트워크 예외입니다).이 경우, 도메인 고유의 예외로 래핑 할 필요가 있습니다.
한편, 예외로 인해 프로그램에서 복구할 수 없는 오류가 발생한 경우(즉, 이 예외의 최종 결과는 프로그램 종료가 됩니다) 저는 개인적으로 이를 포착하여 런타임 예외를 발생시킴으로써 명확하게 하고 싶습니다.
예에서 코드 패턴을 사용하는 경우 TryDoSomething이라고 하고 특정 예외만 포착합니다.
또한 진단을 위해 예외를 기록할 때 예외 필터를 사용하는 것도 고려하십시오.VB는 예외 필터에 대한 언어를 지원합니다.Greggm 블로그 링크에는 C#에서 사용할 수 있는 구현이 있습니다.예외 필터는 캐치 및 재투기에 대한 디버깅 가능성에 대해 더 나은 속성을 가집니다.특히 필터에 문제를 기록하고 예외가 계속 전파되도록 할 수 있습니다.이 방법을 사용하면 JIT(Just in Time) 디버거를 연결하여 원래 스택 전체를 가질 수 있습니다.재투입하면 재투입된 지점에서 스택이 절단됩니다.
TryXXXX가 적절한 경우는 서드파티 함수를 랩하고 있는 경우입니다.서드파티 함수는 그다지 예외적이지 않거나 함수를 호출하지 않고 테스트하기 어려운 경우가 있습니다.예를 들어 다음과 같습니다.
// throws NumberNotHexidecimalException
int ParseHexidecimal(string numberToParse);
bool TryParseHexidecimal(string numberToParse, out int parsedInt)
{
try
{
parsedInt = ParseHexidecimal(numberToParse);
return true;
}
catch(NumberNotHexidecimalException ex)
{
parsedInt = 0;
return false;
}
catch(Exception ex)
{
// Implement the error policy for unexpected exceptions:
// log a callstack, assert if a debugger is attached etc.
LogRetailAssert(ex);
// rethrow the exception
// The downside is that a JIT debugger will have the next
// line as the place that threw the exception, rather than
// the original location further down the stack.
throw;
// A better practice is to use an exception filter here.
// see the link to Exception Filter Inject above
// http://code.msdn.microsoft.com/ExceptionFilterInjct
}
}
TryXX와 같은 패턴을 사용할지 여부는 스타일 질문에 가깝습니다.모든 예외를 잡아서 삼키는 문제는 스타일의 문제가 아닙니다.예기치 않은 예외가 전파되도록 허용해야 합니다!
사용하는 언어에 대해 표준 라이브러리에서 힌트를 얻을 것을 권장합니다.C#에 대해서는 말할 수 없지만 Java에 대해 알아보겠습니다.
예를 들어 java.lang.reflect 입니다.어레이에 정전기가 발생.set
방법:
static void set(Object array, int index, Object value);
C방식은
static int set(Object array, int index, Object value);
...환원값이 성공 지표가 됩니다.하지만 넌 더 이상 C세상이 아니야.
예외를 받아들이면 에러 처리 코드를 코어 로직에서 멀어지게 함으로써 코드를 심플하고 명확하게 할 수 있습니다.한 번에 많은 문장을 포함하는 것을 목표로 합니다.try
차단합니다.
다른 사람들이 지적한 바와 같이, 가능한 한 구체적으로 예외를 포착해야 합니다.
예외를 포착하고 false를 반환하려면 매우 구체적인 예외가 필요합니다.안 그러면 다 잡아서 거짓으로 돌려보내는 거야MyCarIsOnFireException이 나오면 바로 알고 싶어요!나머지 예외는 신경 안 쓸 수도 있어요따라서 일부 예외(무슨 일이 일어났는지 더 잘 설명하는 새로운 예외를 다시 찾거나 다시 던짐)에 대해 "woa woa something here"라고 말하고 다른 예외 핸들러에 대해서는 false를 반환하는 예외 핸들러를 스택해야 합니다.
이 제품이 출시될 경우 이러한 예외를 기록해야 합니다. 향후 조정에 도움이 됩니다.
편집 : 트라이/캐치로 모든 것을 포장하는 질문에 대해서는, 「그렇다」라고 생각합니다.예외는 코드 내에서 매우 드물기 때문에 캐치 블록 내의 코드가 실행되는 경우는 거의 없기 때문에 성능에 전혀 영향을 주지 않습니다.예외는 스테이트 머신이 고장나서 어떻게 해야 할지 모르는 상태입니다.적어도 그때의 상황을 설명하고, 그 안에 예외가 포착된 예외를 재투입합니다.「 Exception in method doSomeStuff() 」는, 휴가중(또는 새로운 작업중)에 그것이 고장난 이유를 알아내야 하는 사람에게는 그다지 도움이 되지 않습니다.
전략:
원래 함수가 void를 반환하면 return bool로 변경합니다.예외/오류가 발생하면 false를 반환하고 이상이 없으면 true를 반환합니다.
함수가 무언가를 반환해야 하는 경우 예외/오류가 발생하면 null을 반환하고 그렇지 않으면 반환 가능한 항목을 반환합니다.
bool 대신 오류 설명을 포함하는 문자열을 반환할 수 있습니다.
반환하기 전에 모든 경우에 오류를 기록합니다.
여기 훌륭한 답변이 있습니다.덧붙이고 싶은 것은, 만약 당신이 투고한 것과 같은 것이 되어 버린다면, 적어도 스택 트레이스보다 더 많은 것을 인쇄해 주세요.그때 무엇을 하고 있었는지와 Ex.get Message()를 말하고 개발자에게 싸울 기회를 줍니다.
try/displocks는 첫 번째 (메인) 세트에 내장된 두 번째 로직 세트를 형성하기 때문에 읽을 수 없고 디버깅하기 어려운 스파게티 코드를 두드리기 위한 훌륭한 방법입니다.
그래도 합리적으로 사용해도 가독성이 뛰어나지만 다음 두 가지 간단한 규칙을 따라야 합니다.
라이브러리 처리 문제를 파악하여 메인 논리 흐름으로 스트리밍하기 위해 하위 수준에서 이러한 문제를 사용합니다.우리가 원하는 오류 처리의 대부분은 데이터 자체의 일부로 코드 자체에서 발생해야 합니다.반환되는 데이터가 특별하지 않다면 왜 특별한 조건을 만들까요?
상위 레벨에서 하나의 큰 핸들러를 사용하여 하위 레벨에서 포착되지 않은 코드에서 발생하는 이상한 조건의 일부 또는 모두를 관리합니다.오류(로그, 재시작, 복구 등)에 대해 유용한 작업을 수행합니다.
이 두 가지 유형의 오류 처리를 제외한 나머지 코드는 모두 자유롭고 시도/캐치 코드와 오류 개체가 없어야 합니다.이렇게 하면 어디에 사용하든, 무엇을 사용하든 간에 심플하고 예상대로 작동합니다.
폴.
답변이 조금 늦어질 수 있지만 오류 처리는 시간에 따라 항상 변화하고 진화할 수 있는 것입니다.만약 당신이 이 주제에 대해 더 읽고 싶다면, 나는 그것에 대해 나의 새로운 블로그에 글을 썼다.http://taoofdevelopment.wordpress.com
해피 코딩.
언급URL : https://stackoverflow.com/questions/409563/best-practices-for-exception-management-in-java-or-c-sharp
'sourcecode' 카테고리의 다른 글
작업 작성자의 Redx 상태에 액세스하시겠습니까? (0) | 2022.09.17 |
---|---|
MySQL "ERROR 1005 (HY000)" :'foo' 테이블을 만들 수 없습니다.#sql-12c_4' (오류번호: 150) (0) | 2022.09.17 |
UNIX 소켓 파일용 mysqld_safe 디렉토리 '/var/run/mysqld'가 없습니다. (0) | 2022.09.17 |
Java 웹 응용 프로그램에 사용하는 아키텍처에 대해 설명하십시오. (0) | 2022.09.17 |
val()과 text()의 차이 (0) | 2022.09.17 |