From b83d9d532be8144a724164f08ff2b5f3fda2c489 Mon Sep 17 00:00:00 2001 From: Semi kang <71436576+semi-cloud@users.noreply.github.com> Date: Sun, 10 Sep 2023 22:42:07 +0900 Subject: [PATCH] =?UTF-8?q?[Item68]:=20=EC=9D=BC=EB=B0=98=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=86=B5=EC=9A=A9=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=AA=85=EB=AA=85=20=EA=B7=9C=EC=B9=99=EC=9D=84=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B4=EB=9D=BC=20(#157)(=EC=84=B8=EB=AF=B8)=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Item68]: 일반적으로 통용되는 명명 규칙을 따르라 (#157)(세미) * [Item73]: 추상화_수준에_맞는_예외를_던지라.md --- ...4_\353\224\260\353\245\264\353\235\274.md" | 67 +++++++++++++++++ ...4_\353\215\230\354\247\200\353\235\274.md" | 72 +++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 "Ch09/item68/\354\235\274\353\260\230\354\240\201\354\234\274\353\241\234_\355\206\265\354\232\251\353\220\230\353\212\224_\353\252\205\353\252\205_\352\267\234\354\271\231\354\235\204_\353\224\260\353\245\264\353\235\274.md" create mode 100644 "Ch10/item73/\354\266\224\354\203\201\355\231\224_\354\210\230\354\244\200\354\227\220_\353\247\236\353\212\224_\354\230\210\354\231\270\353\245\274_\353\215\230\354\247\200\353\235\274.md" diff --git "a/Ch09/item68/\354\235\274\353\260\230\354\240\201\354\234\274\353\241\234_\355\206\265\354\232\251\353\220\230\353\212\224_\353\252\205\353\252\205_\352\267\234\354\271\231\354\235\204_\353\224\260\353\245\264\353\235\274.md" "b/Ch09/item68/\354\235\274\353\260\230\354\240\201\354\234\274\353\241\234_\355\206\265\354\232\251\353\220\230\353\212\224_\353\252\205\353\252\205_\352\267\234\354\271\231\354\235\204_\353\224\260\353\245\264\353\235\274.md" new file mode 100644 index 0000000..bbb9614 --- /dev/null +++ "b/Ch09/item68/\354\235\274\353\260\230\354\240\201\354\234\274\353\241\234_\355\206\265\354\232\251\353\220\230\353\212\224_\353\252\205\353\252\205_\352\267\234\354\271\231\354\235\204_\353\224\260\353\245\264\353\235\274.md" @@ -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 접근자 메서드에서 앞 단어를 뺀 형태 ++ 이외의 타입 : 명사 / 명사구 사용 diff --git "a/Ch10/item73/\354\266\224\354\203\201\355\231\224_\354\210\230\354\244\200\354\227\220_\353\247\236\353\212\224_\354\230\210\354\231\270\353\245\274_\353\215\230\354\247\200\353\235\274.md" "b/Ch10/item73/\354\266\224\354\203\201\355\231\224_\354\210\230\354\244\200\354\227\220_\353\247\236\353\212\224_\354\230\210\354\231\270\353\245\274_\353\215\230\354\247\200\353\235\274.md" new file mode 100644 index 0000000..a5542b0 --- /dev/null +++ "b/Ch10/item73/\354\266\224\354\203\201\355\231\224_\354\210\230\354\244\200\354\227\220_\353\247\236\353\212\224_\354\230\210\354\231\270\353\245\274_\353\215\230\354\247\200\353\235\274.md" @@ -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 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` 같은 로깅 기능을 활용하여 기록해두자. + + +> 📚 **핵심 정리**
+아래 계층의 예외를 예방하거나 스스로 처리할 수 없고, 상위 계층에 그대로 노출하기 곤란하다면 예외 번역을 사용하자. 이때 예외 연쇄를 이용하면 상위 계층에는 맥락에 어울리는 고수준 예외를 던지면서, 근본 원인도 함께 알려주어 오류를 분석하기에 좋다.