메서드를 올바르게 사용하려면, 메서드에서 던져지는 예외에 대한 설명이 문서에 있어야 한다. 따라서 예외 문서화는 아주 중요하다

 점검지정 에외는 독립적으로 선언하고, 해당 예외가 발생하는 상황은 Javadoc @throws 태그를 사용해서 정확하게 밝혀라. 하위 예외 클래스를 여럿 거느린 상위 예외 클래스의 이름을 메서드 선언부에 나열하면 안된다는 것이다. 극단적인 예로 "throws Exception" , "throws Throwable" 등이 있다.


 Javadoc @throws 태그를 사용해서 메서드에서 발생 가능한 모든 무점검 예외에 대한 문서를 남겨라. 하지만 메서드 선언부의 throws 뒤에 무점검 예외를 나열하진 마라. 그래야 API 사용자가 무엇이 점검지정 예외이고 무엇이 무점검 예외인지 알 수 있다. 그 둘의 임무가 서로 다르기 때문에 구분해서 예외를 알려줘야 한다. 


요약

메서드가 던질 가능성이 있는 모든 예외를 문서로 남겨라. 점검 지정 예외뿐만 아니라, 무점검 예외에도 문서를 만들라. 일반 메서드뿐 아니라, abstract메서드에도 문서를 만들라. 점검지정 예외는 메서드의 throws 절에 나열하고, 무점검 예외는 throws 절에는 적지마라. 메서드가 던질 가능성이 있는 예외에 대해 문서를 남기지 않으면, 다른 사람이 효과적으로 사용할 수 있는 클래스나 인터페이스는 만들기 힘들거나,어렵다


상위 계층에서는 하위 계층에서 발생하는 예외를 반드시 받아서 상위 계층 추상화 수준에 맞는 예외로 바꿔서 던져야 한다. 이 숙어를 예외 변환이라 한다. 추상화 수준에 맞지 않는 예외를 던지게 되면, 메서드가 하는 일과 뚜렷한 관련성 없는 예외가 메서드에서 발생하기 때문이다. 추상화 수준이 낮은 곳에서 발생한 에외를 그대로 밖으로 전달하면 이러한 문제가 생긴다. 즉 추상화 수준이 높은 API가 오염되는 것이다. 



예외 연결은 예외 변환의 특별한 사례다. 하위 계층에서 발생한 예외 정보가 상위 계층 외를 발생시킨 문제를 디버깅하는 데 유용할 때 사용된다. 하위 계층에서 발생한 예외(원인)는 상위 계층 예외로 전달된다.


예외 연결을 사용하면 프로그램 안에서 예외의 원인에 접근할 수 있을 뿐 아니라, 최초에 발생한 예외의 스택 추적 정보를 상위 계층 예외에 통합할 수 있다. 하지만 예외변환 기법을 남용하면 안된다. 가능하다면 제일 좋은 방법은 하위 계층에서 예외가 생기지 않도록 하는 것이다. 하위 계층 메서드에 인자를 전달하기 전에 인자 유효성을 미리 검사하는 것도 좋은 방법이다.

요약

하위 계층에서 발생하는 예외를 막거나 처리할 수 없다면, 상위 계층에 보여주면 곤란한 예외는 예외 변환을 통해 처리하라는 것이다. 예외 연결 패턴을 활용하면 적절한 상위 계층 에외를 보여주면서도 하위 계층에서 실제로 발생한 문제까지 확인할 수 있다.




이미 있는 예외들을 재사용하면 좋은 점이 많다.

  • 배우기 쉽고 사용하기 편리한 API를 만들 수 있다. 다른 프로그래머들도 친숙하고, 널리 퍼진 관습을 따르기 때문이다
  • 가독성 높은 API가 될 수 있다. 잘 모르는 예외가 뒤섞여 있지 않기 떄문이다.
  • 예외 클래스 개수를 줄이면 프로그램의 메모리 요구량이 줄어들고, 클래스를 로딩하는 시간도 줄어든다는 것

예외 

용례 

IllegalArgumentException 

null이 아닌 인자의 값이 잘못되었을  때

IllegalStateException 

객체 상태가 메서드 호출을 처리하기에 적절치 않을 때 

NullPointerException 

null 값을 받으면 안 되는 인자에 null이 전달 되었을 때 

IndexOutOfBoundsException 

인자로 주어진 첨자가 허용 범위를 벗어 났을 때 

ConcurrentModificationException 

병렬적 사용이 금지된 객체에 대한 병렬 접근이 탐지 되었을 때 

UnsupportedOperationException 

객체가 해당 메서드를 지원하지 않을 때 


재사용 할 수 있는 예외라 생각된다면, 재사용하면 된다. 하지만 예외를 발생시키는 조건이 해당 예외의 문서에 기술 된 것과 일치해야 한다. 애매한 상황에 대한 예외처리를 해야 할 때, 어떤 예외를 재사용하면 좋을지 결정하는 것은 엄밀한 과학적 절차를 따르지 않는다.

점검지정 예외는 프로그래머로 하여금 예외적인 상황을 처리하도록 강제함으로써 안전성을 높인다. 너무 남발하면 사용하기 불편한 API가 될수도 있다는 뜻이다. 처리해야 하는 예외상황이 많아지니까 !


 메서드가 던지는 점검지정 예외가 하나뿐일 때 하나의 예외때문에 try문안에 메소드를 호출해야 하는 것이 불편할 것이다. 이러한 상황에 처하면, 점검지정 예외를 없앨 방법이 없을지 고민해봐야 한다. 

 

 점검지정 예외를 무점검 예외로 바꾸는 한 가지 방법은 에외를 던지는 메서드를 둘로 나눠서 첫 번째 메서드가 boolean 값을 반환하도록 만드는 것이다.


 아래 방식으로 구현한 방법이 더 깔끔하다고 하기는 그렇지만 유연한 코드임에는 틀림없다. 프로그래머에게 예외처리를 강제시키지 않으니까? 하지만 이러한 방법은 외부에서 객체 상태를 바꿀 수 있는 객체라면 사용할 수 없다. 


자바는 세 가지 종류의 'throwable'을 제공한다. 점검지정 예외(checked exception), 실행시점 예외(runtime exception), 그리고 오류(error)다.  점검지정 예외를 사용할 것인지 아니면 무점검 예외를 사용할 것인지에 대해 대한 가장 기본적인 규칙은 호출자 측에서 복구할 것으로 여겨지는 상황에 대해서는 점검지정 예외를 이용해야 한다는 것이다. 따라서 메서드에 선언된 점거지정 예외는 메서드를 호출하면 해당 예외와 관계된 상황이 발생할 수 있음을 API 사용자에게 알리는 구실을 한다.

 

무점검 throwable에는 런타임 예외와 오류 두가지있으며, 동작 방식은 같다. 둘다 catch로 처리할 필요가 없으며, 일반적으로는 처리해서도 안된다.프로그램이 무점검 예외나 오류를 던진다는 것은 복구가 불가능한 상황에 직면했다는 뜻이다.이런 예외를 catch하지 않는 스레드는 적절한 오류메세지를 출력하며 종료된다.


 프로그래밍 오류를 표현할 때는 실행시점 예외를 사용해라. 예를 들어 배열이용에 관한 규약을 위반하면 예외가 발생하게 된다. 

요약

복구 가능한 상태에는 점검지정 예외를 사용하고, 프로그래밍 오류를 나타내고 싶을 때는 실행시점 예외를 사용하라는 것이다. 


 위의 코드는 무한루프를 돌면서 배열 범위 밖에 있는 요소를 참조하는 순간 발생하는 ArrayIndexOutOfBoundsException 예외를 감지하고 무시하는 과정을 통해 종료된다. 아래와 같이 작성하는 것이 좀 더 이해하기 쉬울 것이다.



위의 보기와 같이, 예외를 사용해 구현한 순환문은 코드의 원래 목적을 흐리고 성능을 떨어뜨릴 뿐 아니라, 올바른 동작을 보장할 수 없다. 예외는 예외적인 상황에만 사용해야 한다. 평상시 제어 흐름에 이용해서는 안된다.



자바의 작명 관습은 두 가지 범주로 나눌 수 있다. 철자에 관한 것이 하나고 문법에 관한 것이 하나다.


철자에 관계된 작명 관습

패기지 이름은 마침표를 구분점으로 사용하는 계층적 이름이어야 한다. 패키지 이름을 구성하는 각각의 컴포넌트는 알파벳 소문자로 구성하고, 숫자는 거의 사용하지 않는다. 해당 조직 바깥에서 이용될 패키지 이름은 해당 조직의 인터넷 도메인 이름으로 시작해야 하는데, edu.cmu , com.sun 처럼 최상위 도메인 이름이 먼저 온다. 예외적으로 java와 javax는 표준라이브러리와 그 옵션 패키지명이므로 둘은 사용해서는 안된다.

enum, 어노테이션 자료형, 클래스나 인터페이스 이름은 하나 이상의 단어로 구성된다. 각 단어의 첫 글자는 대문자로 약어 사용은 피해야 한다.

메서드나 필드의 이름은 클래스나 인터페이스 이름과 동일한 철자 규칙을 따르며, 다만 첫 글자는 소문자로 한다. remove나 ensureCapacity 등이 그 예다. 예외로는 상수 필드같은 경우는 하나 이상의 대문자 단어로 구성되며, 단어 사이에는 밑줄 기호( _ )를 둔다. 

지역 변수 이름은 멤버 이름과 같은 철자 규칙을 따르며, 약어가 허용 된다.

자료형 인자의 이름은 보통 하나의 대문자다. 가장 널리 쓰이는 것은 다섯가지로, 임의 자료형인 경우엔 T, 컬렉션의 요소 자료형인 경우에는 E, 맵의 키와 값에 대해서는 각각 K와V, 예외인 경우에는 X를 사용한다.


문법적 작명 관습

가변적이며 철자 관습에 비해 논쟁의 여지가 많다. 패키지의 경우에는 문법적 관습이라 할 만한 것이 없다. 

enum자료형을 비롯한 클래스, 인터페이스에는 단수형의 명사나 명사구가 이름으로 붙는다. 인터페이스 같은 경우에는 able이나 ible같은 형용사 어미가 붙기도 한다.

어떤 동작을 수행하는 메서드는 일반적으로 동사나 동사구를 포함한 이름으로 갖는다. boolean값을 반환하는 메서드는 is, 드믈게는 has 등으로 시작한다. 객속성을 반환하는 메서드는 보통 명사나 명사구, 또는 get으로 시작하는 동사구를 이름으로 한다. size, hashCode, getTime 등이 그런 이름이다. 객체의 자료형을 변환하는 메서드, 다른 자료형의 독립적 객체를 반환하는 메서드에는 보통 toType형태의 이름을 붙인다. toString,toAArray 같은 이름이 그 예다. 인자로 전달받은 객체와 다른 자료형의 뷰 객체를 반환하는 메서드에는 asType 형태의 이름을 붙인다. asList 같은 이름이 그 예이다. 호출 대상 객체와 동일한 기본 자료형 값을 반환하는 메서드에는 typeValue와 같은 형태의 이름을 붙인다. intVaule가 그 예다. 정적 팩터리 메서드에는 valueOf, of, getInstance와 같은 이름을 붙인다.

요약

표준적 작명 관습을 내면화시키고, 사용해야 한다



최적화는 좋을 때보다 나쁠 때가 더 많으며, 섣불리 시도하면 더더욱 나쁘다는 것이다. 잘못된 최적화는 빠르지도 않고 정확하지도 않으며 쉽게 고칠 수 없는 시스템으로 이어진다.

 성능 때문에 구조적인 원칙을 희생하지 마라. 빠른 프로그램이 아닌, 좋은 프로그램을 만들려고 노력하라.  좋은 프로그램은 정보 은닉 원칙을 지킨다. 설계에 관한 결정은 각 모듈 내부적으로 내려진다. 따라서 그 설게는 시스템의 다른 부분에는 영향을 주지 않으면서 독립적으로 변경될 수 있다.


 설계를 할 떄는 성능을 제약할 가능성이 있는 결정들은 피하라. 설계 가운데 성능에 문제가 있다는 사실이 발견된 후에 고치기가 가장 까다로운 부분은, 모듈간 상호 작용이나 외부와의 상호작용을 명시하는 부분이다. 그 중 API, 통신 프로토콜, 지속성 데이터 형식 등이 있다. 또한 API를 설계할 때 내리는 결정들이 성능에 어떤 영향을 끼칠지를 생각해 봐야 한다. public을 사용하면 방어적 복사를 너무 많이하게 되며, 인터페이스가 아닌 구현 클래스를 사용해 버리면 api가 특정 구현에 종속되므로 개선할 수 가 없게 된다.


 최적화를 시도할 때마다, 전후 성능을 측정하고 비교해봐야 한다. 이러한 성능 비교는 프로파일링 도구를 활용하면 어디를 최적화할지 좀 더 쉽게 결정할 수 있다.

요약

빠른 프로그램을 만드려고 애쓰지 말라는 것이다. 대신 좋은 프로그램을 짜기 위해 애써야 한다. 그렇다면 성능은 뒤따라 올 것이다. 하지만 시스템을 설계 할 때, 특히 API나 통신 프로토콜, 또는 지속성 데이터 형식을 설계할 때는 성능 문제를 따져봐야 한다.


+ Recent posts