Study/Effective-Java
[item#15] 클래스와 멤버의 접근 권한을 최소화하라
728x90
반응형
구현과 API를 분리하는 정보 은닉
의 장점
- 시스템 개발 속도를 높인다. (여러 컴포넌트를 병렬로 개발할 수 있기 때문에) => 인터페이스를 만들고 병렬로 구현체 개발
- 시스템 관리 비용을 낮춘다. (컴퍼넌트를 더 빨리 파악할 수 있기 때문에) => 인터페이스 위주로 코드를 살펴보면 파악이 빠르고, 문제가 생겼을 때 디버깅 빠르게 할 수 있다.
- 성능 최적화에 도움을 준다. (프로파일링을 통해 최적화할 컴포넌트를 찾고 다른 컴포넌트에 영향을 주지 않고 해당 컴포너트만 개선할 수 있기 때문에) => 성능의 병목지점을 찾아내는게 용의하는 뜻이다.
- 소프트웨어 재사용성을 높인다. (독자적인 컴포넌트라면) => 어떤 컴포넌트가 다른 곳에서도 사용할수 있는 컴포넌트라면
- 시스템 개발 난이도를 낮춘다. (전체를 만들기 전에 개별 컴포넌트를 검증할 수 있기 때문에) => divide and conquer (분할 정복)
클래스와 인터페이스의 접근 제한자 사용 원칙
- 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.
- 톱레벨 클래스와 인터페이스에 package-private 또는 public을 쓸 수 있다.
- public으로 선언하면 API가 되므로 하위 호환성을 유지하려면 영원히 관리해야한다.
- 패키지 외부에서 쓰지 않을 클래스나 인터페이스라면 package-private으로 선언한다.
- 한 클래스에서만 사용하는 package-private 클래스나 인터페이스는 해당 클래스에 private static으로 중첩 시키자 (item 24)
top level이기 때문에 public, package-private으로밖에 사용 불가
Spring IOC Container, Service Loader등, interface만으로 사용하게 하고싶을때 구현체는 package-private으로 만든다.
public interface MemberService {
}
public class Member {
}
class DefaultMemberService implements MemberService {
// item은 MemberService의 어떤 구현체인지를 알필요가 없다.
// 인터페이스만으로 코딩을 하길 바란다.
}
한 클래스에서만 사용하는 package-private 클래스나 인터페이스는 해당 클래스에 private static으로 중첩 시키자
interface MemberRepository {
}
class DefaultMemberService implements MemberService {
// item은 MemberService의 어떤 구현체인지를 알필요가 없다.
// 인터페이스만으로 코딩을 하길 바란다.
MemberRepository memberRepository;
public Member getMember(){
return memberRepository.findById();
}
}
위와 같은 MemberRepository를 DefaultMemberServices 내에서만 사용한다고 했을 때, 밑에와 같이 사용 가능.
class DefaultMemberService implements MemberService {
// item은 MemberService의 어떤 구현체인지를 알필요가 없다.
// 인터페이스만으로 코딩을 하길 바란다.
private String name;
private static class PrivateStaticClass {
// 왜 static일까?
// inner class지만 외부 클래스의 레퍼런스가 없다.
void doPrint() {
System.out.println(name); // compile error
}
}
private class PrivateClass {
// 외부 인스턴스를 참조한다.
// 항상 자동으로 인스턴스를 참조하는 필드가 생김
void doPrint() {
System.out.println(name);
}
}
}
멤버(필드, 메서드, 중첩 클래스/인터페이스)의 접근 제한자 원칙
- private과 package-private은 내부 구현
- public 클래스의 protected와 public은 공개 API -> 접근제한을 풀어주는게 많다면 컴포넌트를 잘못 설계한 것은 아닌가 고민..
- 코드를 테스트 하는 목적으로 private package-private으로 풀어주는 것은 허용할 수 있다. 하지만 테스트만을 위해서 멤버를 공개 API로 만들어서는 안된다. (테스트를 같은 패키지에 만든다면 그럴 필요도 없다.)
- public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.(item 16)
- 클래스에서 public static final 배열 필드를 두거나, 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다.
상수를 제외한 인스턴스 필드는 되도록 public이 아니어야한다.
public class ItemService {
public static final String NAME = "whiteship";
private MemberService memberService; // 인스턴스 필드는 되도록 public이 아니어야한다.
boolean onSale;
protected int saleRate;
public ItemService(MemberService memberService) {
this.memberService = memberService;
}
}
class DefaultMemberService implements MemberService {
private static class PrivateStaticClass {
}
}
test
public class ItemServiceTest {
MemberService memberService;
@Test
void itemService(){
ItemService service = new ItemService(memberService);
}
}
위 코드에서 MemberService의 구현체인 DefaultMemberService를 만들수가 없다.(package-default class이기 때문)
이럴경우, Mocking을 사용한다.
@ExtendWith(MockitoExtension.class)
public class ItemServiceTest {
@Mock
MemberService memberService;
@Test
void itemService(){
ItemService service = new ItemService(memberService);
assertNotNull(service);
assertNotNull(service.getMemberService()); // private MemberService memberService;
}
}
위 처럼 test목적으로 getter라는 공개 API를 만드는 것 보다는 차라리 MemberService를 package-private으로 만들수 있다.
test 폴더라도 package명이 같다면 접근 가능하다.!!!!
위와 같은 방법으로 필드는 private으로 getter는 package-private으로 만들어서 접근 가능하다~!!.
클래스에서 public static final 배열 필드를 두거나, 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다.
public static final String[] NAME = new String[10];
외부에서 NAME상수를 변경 가능하다.
필드들은 전부 private으로 만들고, test를 하다가 private을 package-private으로 변경할 순 있다.
하지만 test를 위해 private을 public이나 protected로 만들진 말자
728x90
반응형
'Study > Effective-Java' 카테고리의 다른 글
[item#17] 변경 가능성을 최소화하라 (1) | 2023.11.29 |
---|---|
[item#16] public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2023.11.29 |
[item#14] Comparable을 구현할지 고려하라 (0) | 2023.11.29 |
[item#13] clone 재정의는 주의해서 진행하라 (1) | 2023.11.29 |
[item#12] toString을 항상 재정의하라 (0) | 2023.11.29 |
댓글