2017년 12월 11일 월요일

JAVA API 디자인, 5-6장

http://charlie0301.blogspot.kr/2017/12/java-api.html

책이 API 디자인 외에 한번쯤 library, framework을 고민한다면 읽어 볼 만함.
일부만 추려서 적었지만 책에는 더 좋은 내용이 자~알 설명 되어 있음.

------------------------------------------------------------------------------------
5장 : 필요 이상으로 노출하지 마라.

무언가를 API로 만들것이냐 만들지 말 것이냐를 결정하는데 유스케이스를 만들어 보라
특정 메서드나 클래스에 대한 유스 케이스가 없거나 미심쩍다면 API에 해당 요소를 빼는 편이 낫다.
API를 처음 릴리스하기 앞서 제거할 수 있는 것은 모조리 제거하라는 것이다.


1. 필드를 감춰라, 직접 노출하기 보다는 필드에 접근하는 메서드(getter, setter)를 사용하는 편이 낫다.
읽기, 쓰기만 가능한 필드 보다 추가작업을 할 수 있다.

2. 생성자보다 팩터리가 낫다.
생성자 대신 팩터리 메서드를 노출할 경우 나중에 API를 진화시키기 수월해진다.

 1. 팩터리의 반환 클래스 타입을 유연(다형성)하게 제공할 수 있다.
 2. 인스턴스를 캐싱할 수 있다.
 3. 동기화가 가능하다.

3. 모든 것을 final로 만들어라
서브 클래싱을 허용하는데는 생각치 못한 결과가 발생할 수 있으므로 class를 final로 만들어라.

4. 어울리지 않는 곳에 설정자 메서드를 넣지 마라.
설정자(setXXXX)는 구현 세부사항을 API 사용자가 아는 상황에서 사용 가능하다.
API의 기능을 풍부하게 만들기 위한 방법으로 주로 사용되는데 역효과(유지보수, API 시맨틱 파악 어려움)가 발생한다.

5. 프랜드 코드에서만 접근하는 것을 허용하라.
API에서 너무 많은 것들을 호출하지 않으려고 할 때 또 한가지 유용한 기법은 특정 기능에 대한 접근 권한을 "프랜드(friend)" 코드에만 부여하는 것이다.

- "Only for internal use", "Do not call me" 이딴 설명을 API에 적지 말고 접근권한 변경으로 사용자에게서 숨겨라.

6. 객체를 만든 이에게 더 많은 권한을 부여하라.
(TBD)

7. 깊은 계층구조를 노출하지 마라.
(TBD)


------------------------------------------------------------------------------------
6장 : 구현이 아닌 인터페이스를 대상으로 코드를 작성하라.

1. 메서드나 필드 제거하기

일단 공개된 인터페이스나 클래스에서 메서드나 필드를 제거하는 것은
기존 API 사용 코드들에서 컴파일 에러를 발생 시키고 나아가서 바이너리 호환성도 보장하지 않는다.

private, package private, protected abstract(설계 실수)는 제거 가능
public, protected(설계 실수) 는 제거 불가


2. 클래스나 인터페이스를 제거하거나 추가하기

제거는 소스및 바이너리 차원에서 문제를 일으킴, 공개된 것들은 계속 유지하는 것을 권고
추가는 문제 없음. 다만 와일드카드 임포트를 이용할 경우 소스 호환성이 깨질 수 있음.


3. 기존 계층 구조에 인터페이스나 클래스 집어 넣기

가능, 제약 조건은 원본 및 새로운 타입에서 접근할 수 있었던 메서드 전체가 동일하게 유지되거나 최소한 줄어들지 않아야함.


4. 메서드나 필드 추가하기

필드의 경우 앞서 말한 것과 같이 바이너리 호환성을 지원할 수 있는 static final이 아닌 이상 API에 추가해서는 안된다.

정적 메서드를 추가하는 것은 바이너리 관점에서 수용할만하지만 클래스에서만 사용해야 한다.
정적 메서드와 중복 정의된 비슷한 메서드의 변종들로 비호환성을 야기하거나 컴파일 오류를 만들 수 있다.

서브클래싱이 가능한 클래스에 추상 메서드를 추가하는 것은 호환성을 깨뜨리는 변경사항에 해당됨. 이는 인터페이스를 사용하는 구현에서 런타임 예외를 발생 시킬 수 있다.
즉 추상메서드를 서브클래싱이 가능한 클래스와 인터페이스에 추가하는 것을 최소화 해야 하던가 클래스와 인터페이스를 서브클래싱 할 수 없게끔 만들어야 한다.

5. 자바 인터페이스와 클래스 비교

자바 인터페이스의 가장 큰 기능은 다중 상속이지만 실제로 성능, 점유 메모리의 양을 최소화 하기 위함이다.
다중 상속이면 하나의 객체로 API 인터페이스를 무제한 상속 가능하지만 클래스의 경우 API 클래스 마다 하나의 하위 클래스를 생성해야 하고 다른 클래스와 연관된 경우 위임을 통해서 인스턴시를 연결해야 한다.
단 한 두개 보다 상당한 객체들을 생성하는 경우에만 상당한 효과가 있다.


6. 외유내강

인터페이스 관점에서 기존 인터페이스에 메서드를 추가하는 것이 다소 어렵다.
하지만 이를 인터페이스의 강점으로 대처할 수 있는데
각기 버전마다 인터페이스를 만들어 제공하는 것이 방법이 될 수 있다.
Language13, Language14, Lauguage15 와 같이 인터페이스르 만들어 제공하면 사용자는 필요한 것을 선택해 사용함.
단 너무 인터페이스가 급격히 늘어나면 클라이언트 코드가 복잡해 질 수 있다.


7. 메서드를 추가하길 좋아하는 사람들의 천국

메서드가 완전히 바이너리 호환성을 보장하도록 하게 위해서는 final 클래스 형태로 표현한다.
이는 서브클래싱도 막을 수 있고 인터페이스나 추상 클래스에 메서드가 추가됐을 때도 문제는 없지만
추가된 메서드를 호출되는 경우에는 문제가 된다
클래스 파일에서 메서드의 이름과 매개변수, 반환형을 기준으로 호출된 메서드를 완전히 구분하므로
이름과 인자수가 같은 메서드를 추가하지 않아야 문제를 방지할 수 있다.

8. 추상 클래스는 유용한다.
지금까지 불변적인 계약을 정의하고 싶을 때 자바 인터페이스를 사용하고
메서드를 추가할 능력을 갖추고 싶을 때 자바 final 클래스를 사용해야 한다고 주장
반면에 추상 클래스를 사용할 이유는 실제로 없다.

자바 인터페이스와 비교 시 자바 추상 클래스의 한가지 이점은 정적 메서드를 포함할 수 있어 추상 클래스에 정적 팩터리 메서드를 만들 필요성이 있다면 사용 가능하다.

자바 인터페이스에 비해 자바 클래스의 유용한 특징은 접근 권한을 제한할 수 있다.
추상클래스를 만들어 제공할 경우 구현할 수 있는 클래스 제한은 런타임 시 클래스 타입을 보고 제한할 수 있다.

9. 매개변수 증가를 위한 대비
response/reply 패턴?

10. 인터페이스 대 클래스
구현이 아닌 인터페이스(자바 인터페이스가 아니라 추상적인 정의)를 대상으로 코드를 작성하라.
자바 인터페이스를 사용해서 불변적인 타입을 명시하고 메서드를 안전하게 추가할 수 있는 곳에서는 final 클래스를 타입으로 사용한다.
목표가 완전한 바이너리 호환성을 보장하는 것이라면 다른 누군가가 구현할 수 있는 클래스나 인터페이스에 절대 메서드를 추가해서는 안된다.

댓글 없음:

댓글 쓰기