이 포스팅은 공부 목적으로 작성된 포스팅입니다. 왜곡된 내용이 포함되어 있을 수 있습니다.
64. 객체는 인터페이스를 사용해 참조하라
아이템 51에서 매개변수 타입으로 클래스가 아니라 인터페이스를 사용하라는 원칙이 있었다. 이부분을 더 확장하여 적합한 인터페이스가 있는 경우 인터페이스를 통해 반환값, 변수, 필드를 선언하자. 실제 구현체를 사용하는 경우는 생성자의 경우이고 나머지는 인터페이스 메소드로 대체하는 것이다.
// 좋은 예. 인터페이스를 타입으로 사용했다.
Set<Son> sonSet = new LinkedHashSet<>();
// 나쁜 예. 클래스를 타입으로 사용했다.
LinkedHashSet<Son> sonSet = new LinkedHashSet<>();
사실 대부분 인터페이스로 선언하는 규칙은 관례처럼 사용되고 있다. 두번째 선언과 같이 LinkedHashSet<Son> sonset와 같이 구현체를 타입에 명시하면 오히려 어색하게 느껴질 정도이다.
인터페이스를 타입을 사용하는 습관을 길러두면 프로그램이 훨씬 유연해질 것이다.
그러나 원래의 클래스가 인터페이스의 일반 규약 이외의 특별한 기능을 제공하며, 주변 코드가 해당 기능에 기대어 동작한다면, 새로운 클래스도 같은 기능을 제공해야한다.
유지보수 측면에서 바라보면 클래스가 추상화되었기 때문에 오해의 소지가 발생할 수도 있게 되는 것이다.
만약 적합한 인터페이스가 없다면 당연히 클래스로 참조해야 한다. String,Big Integer가 이에 해당하는데, final으로 선언되어 있는 경우가 많다. OutputStream,java.io와 같은 클래스 기반으로 작성된 프레임워크들도 클래스를 참조하여 사용해야한다. 인터페이스가 제공하지 않는 메서드를 제공하는 경우에도 클래스를 참조해야한다. PriorityQueue 클래스는 Queue를 구현하지만, comparator메서드를 제공하는 것과 같이 기존 Queue에서 제공하지 않는 기능을 제공하기 때문에 클래스를 직접사용한다.
글쓴이의 경험으로는 Queue 타입으로 PriorityQueue선언 해도 크게(?) 문제 없는 것으로 알고 있는데 책에서는 Queue의 패러다임을 아에 다시쓴 자료구조(LIFO 위배)라서 인터페이스를 사용하기에는 불안정하다는 의견이다.
65 리플렉션보다는 인터페이스를 사용하라
리플렉션 기능을 이용하면 접근자의 제한 없이 클래스에 접근할 수 있다. 또한 Constructor, Method, Field 인스턴스를 통해 해당 클래스의 인스턴스를 생성하거나, 메서드를 호출하거나, 필드에 접근할 수 있다. 리플렉션을 이용하면 컴파일 당시에 존재하지 않던 클래스도 이용할 수 있는데, 리플렉션은 단점이 있다.
- 컴파일타임 타입 검사가 주는 이점을 하나도 누릴 수 없다. 리플렉션에서 발생하는 애러는 모두 런타임애러가 된다.
- 리플렉션을 이용하면 코드가 지저분해진다. 유지보수 측면에서 손해이다.
- 성능이 떨어진다. 리플렉션은 일반 메서드에 비해 비용이 높다.
리플렉션은 스프링에서 의존관계 주입, JPA에서 엔티티주입과 같은 주입에 사용되고 있다. 이러한 리플렉션은 제한된 형태로만 사용해야 그 단점을 피하고 이점만 취할 수 있다.
try {
s = cons.newInstance();
} catch (InvocationTargetException e) {
// 생성자가 예외를 던졌을 때
e.printStackTrace();
} catch (InstantiationException e) {
// 클래스를 인스턴스화할 수 없을 때
e.printStackTrace();
} catch (IllegalAccessException e) {
// 생성자에 접근할 수 없을 때
e.printStackTrace();
} catch (ClassCastException e) {
// Set을 구현하지 않은 클래스일 때
e.printStackTrace();
}
위 코드와 같이 리플렉션을 사용할 경우 컴파일 타임까지 발생할 수 있는 에러를 코드로 처리해줘야한다.
66 네이티브 메서드는 신중히 사용하라
자바 네이티브 인터페이스는 자바 프로그램이 네이티브 메서드를 호출하는 기술로, C,C++로 작성된 메서드를 말한다.
이러한 네이티브 메서드를 사용하는 경우 다음과 같은 경우이다.
- 네이티브 코드로 작성된 기존 라이브러리를 사용하는 경우
- 성능개선을 위해 네이티브 언어를 도입하는 경우
네이티브 메서드는 기존 자바 메서드보다 더 JVM 시스템 level의 메소드를 제공하고 있을 것이다. 그러나 자바가 발전하면서, 하부 플랫폼 기능을 흡수하고 있어 네이티브 메서드를 사용할 필요가 없어지고 있다. 대표적인 네이티브 메서드는 Thread.currentThread()가 있다.
성능개선의 목적을 네이티브 메서드를 사용하는 것은 권장하지 않는다. 기존에 자바에서 만든 체계보다 더 좋게 만들기는 힘들 뿐만 아니라(새로운 바퀴를 만들지 맙시다) 네이티브 언어는 안전하지 않기 때문이다. 디버깅도 어렵고, 잘못하면 속도가 더 느려질 수 있다. GC는 네이티브 메모리를 회수하지 못하기도 하다.
67 최적화는 신중히 하라
빠른 프로그램보다 좋은 프로그램을 작성하자. 좋은 프로그램이지만 원하는 성능이 나오지 않는다면, 그 아키텍쳐 자체가 최적화할 수 있는 길을 안내해줄 수 있다. 좋은 프로그램은 정보 은닉 원칙을 따르므로 개별 구성요소의 내부를 독립적으로 설계할 수 있다. 성능을 아에 고려하지 않는 것이 아니다. 구현 문제는 최적화할 수 있지만, 아키텍처의 결함이 성능을 제한한다면 시스템을 다시 만드는 것 외에 해결방법이 없을 수 있다. 설계 단계에서 다음을 고려하자
- 성능을 제한하는 설계를 피할 것
- API를 설계할 때, 성능에 주는 영향을 고려 할 것 public 타입을 가변으로 만드는 경우 내부 데이터를 변경할 수 있어 해당 데이터를 리턴할 때마다 방어적 복사를 해줘야한다.
- 성능을 위해 API를 왜곡해선 안된다.
- 최적화 전후 성능을 특정해라
68 일반적을 통용되는 명명 규칙을 따르랴
자바는 명명 규칙이 잘 정리되어 있다. 자바 명명 규칭은 철자와 문법 두가지로 나눌 수 있다.
철자 규칙은 패키지, 클래스, 인터페이스, 메서드, 필드, 타입 변수의 이름 규칙이다. 해당 규칙은 반드시 따라야하고, 어긴경우 API로 사용하기 어려울 뿐더러 유지보수하기 어렵디. 패키지와 모듈 이름은 점을 기준으로 계층적으로 구성한다. 각각의 이름은 모두 소문자를 사용한다. 고직 바깥에도 사용될 패키지라면 조직의 인터넷 도메인 이름을 역순으로 사용한다.(스프링 project를 생성할때 com가 이것이다.) 예외적으로 표준 라이브러리와 선택적 패키지는 java와 javax로 시작한다. 패키지 이름의 나머지는 해당 패키지를 설명하는 하나 이상의 요소로 이루어진다. 각각의 요소는 8자 이하 단어를 사용한다. 너무 길다면 줄임말을 한시적을 허용한다.
메서드와 필드이름은 첫글자를 소문자를 하는 것 이외에는 클래스 명명 규칙과 동일하다. 단 상수 필든는 모두 대문자와 각각의 단어를 밑줄로 구분한다(CONSTANT_SOMETHING)
타입 매개변수는 한문자로 표현한다. 임의의 타입에는 T, 컬렉션 원소의 타입은 E. 맵의 키와 값에는 K,V, 예외에는 X, 메서드 반환 타입에서는 R을 사용한다. 그 외의 임의 타입의 시퀀스에는 T,U,V,T1,T2,T3를 사용한다.
문법 규칙은 철자 규칙에 비해 유연하여 논란이 있다. 패키지에 대한 규칙은 따로없다. 객체를 생성할 수 있는 클래스의 이름은 보통 단수 명사나 명사구를 사용한다. 객체를 생성할 수 없는 클래스의 이름은 보통 복수형 명사를 사용한다(Collections) 인터페이스 이름은 클래스와 똑같이 짓거나 able. ible와 같은 형용사로 짓는다. 에노테이션은 정해진 규칙없이 명사, 형용사 등 다양하게 사용한다. 어떤 동작을 수행하는 메서드의 이름은 동사나 동사구로 짓는다. 반환 타입이 boolean이 아니거나 해당 인스턴스의 속성을 반환하는 메서드는 명사, 명사구 ,get을 시작하는 형태를 사용한다.(빈약한 주자이라고 한다.)
get으로 시작하는 형태는 자바빈즈에서 파생된 것이다.
객체의 타입을 바꾸는 인스턴스 메서드는 보통 toType의 형태로 짓는다. 객체의 내용을 다른 뷰로 보여주는 메서드는 asType형태로 짓는다(asList) 객체의 값을 기본 타입 값으로 반환하는 메서드는 보통 typeValue의 형태로 짓는다.(intValue) 정적 팩터리 메소드는 from,of 등등을 흔히 사용한다.
boolean 타입의 필드 이름은 보통 boolean 접근자 메서드에서 앞 단어를 뺀 형태다(is를 뺀 단어) 다른 타입의필드라면 명사나 명사를 사용한다,
'Java' 카테고리의 다른 글
[이펙티브 자바] 아이템 54, 55 (0) | 2024.05.20 |
---|---|
List.toArray() (0) | 2024.05.19 |
[이펙티브 자바] 아이템 47, 48, 49 (2) | 2024.04.27 |
[이펙티브 자바] 아이템 39, 40, 41 (1) | 2024.03.24 |
[이펙티브 자바] 아이템 10, 11 (1) | 2024.01.14 |