-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* [Item68]: 일반적으로 통용되는 명명 규칙을 따르라 (#157)(세미) * [Item73]: 추상화_수준에_맞는_예외를_던지라.md
- Loading branch information
1 parent
90930a5
commit b83d9d5
Showing
2 changed files
with
139 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
## 🌱 철자 규칙 | ||
### ☁️ 패키지와 모듈 이름 | ||
각 요소를 `.` 으로 구분하여 **계층적**으로 짓는다. | ||
만약 조직 바깥에서도 사용될 패키지라면, 조직의 인터넷 도메인 이름을 역순으로 사용하면 된다. ex) `com.google` | ||
|
||
각 패키지 이름은 소문자를 사용한 8자 이하의 짧은 단어 혹은 **약어**를 추천한다. | ||
`utilities` 보다는 `util` 이 좋다. | ||
|
||
### ☁️ 클래스와 인터페이스 이름 | ||
하나 이상의 단어로 이루어지며, 각 단어는 대문자로 시작해야 한다 | ||
단어의 첫 글자만 딴 약자나 `max`, `min` 처럼 널리 통용되는 줄임말을 제외하고 단어를 줄여 쓰면 안된다. | ||
|
||
### ☁️ 메서드와 필드 이름 | ||
첫 글자를 소문자로 쓴다는 점만 빼면 클래스 명명 규칙과 같다. 만약 첫 단어가 약자라면, 단어 전체가 소문자여야 한다. | ||
|
||
#### 🔖 상수 필드 | ||
단, 상수 필드는 예외적으로 모두 **대문자**로 쓰며 단어 사이는 밑줄로 구분한다. | ||
```java | ||
private static final int NEGATIVE_INFINITY = XX | ||
``` | ||
주의할 점은 `static final` 필드이면서, 가리키는 객체가 불변이라면 비록 그 타입은 가변이더라도 상수 필드라는 것이다. 즉, `static final` 타입이 **기본 타입이나 불변 참조 타입**이라면 상수 필드에 해당한다. | ||
|
||
### ☁️ 타입 매개변수 | ||
타입 매개변수의 이름은 보통 한 문자로 포함한다. | ||
> | ||
`T` : 임의의 타입 | ||
`E` : 컬렉션 원소의 타입 | ||
`K`, `V`: 맵의 키와 값 | ||
`X` : 예외 | ||
`R` : 메서드 반환 타입 | ||
`T`, `U`, `V` (`T1`, `T2`, `T3`) : 임의 타입의 시퀀스 | ||
|
||
## 🌱 문법 규칙 | ||
### ☁️ 클래스 | ||
|
||
보통 **단수 명사나 명사구**를 사용한다. | ||
|
||
1. 객체를 생성할 수 없는 클래스의 이름은 보통 복수형 명사로 짓는다.(ex) `Collectors` ) | ||
2. 인터페이스 이름은 클래스와 똑같거나 `able` 혹은 `ible` 로 끝나는 형용사로 짓는다. (ex) `Runnable`, `Accessible`) | ||
3. 애너테이션은 명사, 동사, 전치사, 형용사가 두루 쓰인다. (ex) `Inject`, `ImplementedBy`) | ||
|
||
### ☁️ 메서드 | ||
어떤 동작을 수행하는 메서드 이름은 **동사나 동사구**로 짓는다. 단, 동사는 **현재형**이여야 한다. (ex) `drawImage` ) | ||
|
||
+ `is`, `has` : `boolean` 값을 반환하는 메서드 (ex) `isDigit`, `hasSiblings`) | ||
|
||
+ `명사`, `get` : 해당 인스턴스의 속성을 반환하면서 반환 타입이 `boolean` 이 아닌경우 (ex) `size`, `getTime` ) | ||
```java | ||
if (car.speed() > 2 * SPEED_LIMIT) | ||
generateAudibleAlert("경찰 조심하세요!"); | ||
``` | ||
|
||
+ `toType` : 객체의 타입을 바꿔서 다른 타입의 다른 객체를 반환하는 인스턴스 메서드 (ex) `toString`, `toArray` ) | ||
|
||
+ `asType` : 객체의 내용을 다른 뷰로 보여주는 메서드 (ex) `asList` ) | ||
|
||
+ `typeValue` : 객체의 값을 기본 타입 값으로 반환하는 메서드 (ex) `intValue` ) | ||
|
||
>🔖 **정적 팩터리 이름** | ||
from, of, valueOf, instance, getInstance, newInstance, getType, newType을 흔히 사용 | ||
|
||
|
||
### ☁️ 필드 이름 | ||
필드 이름에 관한 문법 규칙은 클래스, 인터페이스, 메서드 이름에 비해 덜 명확하고 덜 중요하다. API 설계를 잘 했다면 필드가 직접 노출될 일이 거의 없기 때문이다. | ||
|
||
+ `boolean` 타입 : boolean 접근자 메서드에서 앞 단어를 뺀 형태 | ||
+ 이외의 타입 : 명사 / 명사구 사용 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
메서드가 **저수준 예외를 처리하지 않고 바깥으로 전파**( `throws` )해버릴 때, 수행하려는 일과 관련 없어 보이는 예외가 튀어나온다. 이는 내부 구현 방식을 드러내어 윗 레벨의 API를 오염시킬 수 있으므로 위험하다. | ||
|
||
#### Spring | ||
대표적인 예시로, 스프링에서도 데이터 계층에서 특정 DB에 종속되는 `SQLException` 과 같은 저수준 예외를, 예외 변환기 ( `PersistenceExceptionTranslator`) 를 통해 고수준 `DataAccessException` 으로 변환시킨다. | ||
|
||
### ☁️ 예외 번역(Exception Translation) | ||
이 문제를 피하려면, **상위 계층에서는 저수준 예외를 잡아 자신의 추상화 수준에 맞는 예외로 바꾸어 던져야** 한다. 그리고 이를 **예외 번역**이라 한다. | ||
|
||
```java | ||
try { | ||
... | ||
} catch (LowerLevelException e) { | ||
throw new HigherLevelException(...); | ||
} | ||
``` | ||
|
||
예외 번역을 사용한 예로 `AbstractSequentialList` 를 들어보자. | ||
|
||
#### AbstractSequentialList | ||
|
||
```java | ||
/** | ||
이 리스트 안의 지정한 위치의 원소를 반환한다. | ||
@throws IndexOutOfBoundsException index가 범위 밖이라면, | ||
즉 {@code index < 0 || index >= size()}이면 발생한다. | ||
**/ | ||
public E get(int idex) { | ||
ListIterator<E> i = listIterator(index); | ||
try { | ||
return i.next(); | ||
} catch (NoSuchElementException e) { | ||
throw new IndexOutOfBoundsException("인덱스: " + index); | ||
} | ||
} | ||
``` | ||
|
||
### ☁️ 예외 연쇄(Exception Chaining) | ||
|
||
예외 연쇄란, 문제의 근본 원인인 저수준 예외를 고수준 예외에 실어 보내는 방식이다. 따라서 예외를 번역할 때, 저수준 예외가 디버깅에 도움이 된다면 예외 연쇄를 사용하도록 하자. | ||
|
||
```java | ||
try { | ||
... | ||
} catch (LowerLevelException cause) { | ||
// 저수준 예외를 고수준 예외에 실어 보낸다. | ||
throw new HigherLevelException(cause); | ||
} | ||
|
||
``` | ||
|
||
대부분의 표준 예외는 아래와 같이 예외 연쇄용 생성자를 갖추고 있다. | ||
```java | ||
class HigherLevelException extends Exception { | ||
HigherLevelException(Throwable cause) { | ||
super(cause); | ||
} | ||
} | ||
``` | ||
문제의 원인을 `getCause` 메서드로 프로그램에서 접근할 수 있게 해주어, 원인과 고수준 예외의 스택 추적 정보를 잘 통합해준다. | ||
|
||
### ☁️ 주의 사항 | ||
결론적으로 예외를 전파( `throws` )하는 것보다 예외 번역이 더 나은 방법이지만, 남용해서는 안된다. 우선순위는 다음과 같다. | ||
|
||
1. 가능하다면 저수준 메서드가 반드시 성공해 예외를 발생시키지 않도록 한다. | ||
2. 상위 계층 메서드의 매개변수 값을 아래로 넘기기 전에 미리 검사한다. | ||
3. 예외를 예방 및 처리 불가능할때 예외 번역을 사용한다. | ||
|
||
만약 아래 계층에서의 예외가 불가피하다면, 상위 계층에서 예외를 조용이 처리하여 API 호출자에게까지 전파하지 않는 방법도 존재한다. 이 경우, 발생한 예외는 `java.util.logging` 같은 로깅 기능을 활용하여 기록해두자. | ||
|
||
|
||
> 📚 **핵심 정리**<br> | ||
아래 계층의 예외를 예방하거나 스스로 처리할 수 없고, 상위 계층에 그대로 노출하기 곤란하다면 예외 번역을 사용하자. 이때 예외 연쇄를 이용하면 상위 계층에는 맥락에 어울리는 고수준 예외를 던지면서, 근본 원인도 함께 알려주어 오류를 분석하기에 좋다. |