본문 바로가기
public void static main()/Book

[Effective Java] 아이템 15

by 햄리뮤 2023. 1. 18.
반응형

으아아아 내일 스터디인데 스터디 할꺼 많아져서 블로그를 이제쓴다.... 책 읽기도 힘들었따......

[책 규칙]

노란색 - 자세히 알아보고싶은 부분

초록색 - 핵심 개념 (블로그 정리시는 검은색으로!)

빨간색 - 무슨말인지 모르겠는 부분

[아이템 15] 클래스와 멤버의 접근 권한을 최소화하라

잘 설계된 컴포넌트의 가장 큰 차이는 클래스의 내부 데이터와 내부 구현 정보를 외부 컴포넌트로부터 얼마나 잘 숨겼느냐다. 정보 은닉, 혹은 캡슐화라고 하는 이 개념은 소프트웨어 설계의 근간이 되는 원리이다.

정보은닉(캡슐화)

  • 장점
    • 시스템 개발 속도를 높인다. 여러 컴포넌트를 병렬로 개발할 수 있기 때문이다.
    • 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악하여 디버깅할 수 있고, 다른 컴포넌트로 교체하는 부담도 적기 때문이다.
    • 정보 은닉 자체ㅔ가 성능을 높여주지는 않지만, 성능 최적화에 도움을 준다. 완성된 시스템을 프로파일링해 최적화할 컴포넌트를 정한 다음 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화할 수 있기 때문이다.
    • 소프트웨어 재사용성을 높인다. 외부에 거의 의존하지 않고 독자적으로 동작할 수 있는 컴포넌트라면 그 컴포넌트와 함께 개발되지 않은 낮선 환경에서도 유용하게 쓰일 가능성이 크기 때문이다.
    • 큰 시스템을 제작하는 난이도를 낮춰준다. 시스템 전체가 아직 완성되지 않은 상태에서도 개별 컴포넌트의 동작을 검증할 수 있기 때문이다.
모든 클래스와 맴버의 접근성을 가능 좁혀야 한다.

이번 아이템에서는 주의하는 부분을 정리해보았다!

  • public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다!
  • public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않다.
  • 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안 된다.
    • 이런 필드나 접근자를 제공한다면 클라이언트에서 그 배열의 내용을 수정할 수 있게 된다.

아래 코드는 보안 허점이 숨어있다고한다 (두리번 두리번 어디가 허점이지??)

https://gagstore.tistory.com/715

 

// 보안 허점이 숨어 있다.
public static final Thing[] VALUES = {...};

이 허점이 있는 코드에 대한 해결책은 두 가지다.

첫 번째 방법은 앞 코드의 public 배열을 private으로 만들고 public 불변 리스트를 추가하는 것이다.

private static final Thing[] PRIVATE_VALUES = {...};

public static final List<Thing> VALUES =vCollections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));

두 번째는 배열을 private으로 만들고 그 복사본을 반환하는 public 메서드를 추가하는 방법이다(방어적 복사).

private static final Thing[] PRIVATE_VALUES = {...};

public static final Thins[] values() {
	return PRIVATE_VALUES.clone();
}
방어적 복사
생성자를 통해 초기화하거나 내부의 객체를 반환할 때, 새로운 객체로 감싸서 복사해주는 방법이다. 외부와 내부에서 주소 값을 공유하는 인스턴스의 관계를 끊어주기 위함이다. Collection의 주소만 끊어줬을 뿐, 가지고 있는 각각의 객체들의 주소는 공유되어있다 따라서 깊은 복사는 아니다.
방어적 복사를 사용할 경우, 외부에서 객체를 변경해도 내부의 객체는 변경되지 않는다.
// Date 같은 경우는 값이 변경될 수 있으므로 생성자에서 매개변수의 유효성을 검사하기 전에 방어 복사본을
// 만들고, 이 복사본을 이용해서 유효성을 검증한다. 만약 멀티스레딩 환경일 경우 유효성 검사를 한 후
// 복사본을 만드는 사이에 원본 값이 변경될 위험이 있기 때문디아.
public Period(Date start, Date end) {
    this.start = new Date(start.getTime()); // 방어적 복사
    this.end = new Date(end.getTime()); // 방어적 복사
    if (validation(this.start, this.end)) {
        throw new IllegalArgumentException("");
    }
}
핵심정리
프로그램 요소의 접근성은 가능한 한 최소한으로 하라. 꼭 필요한 것만 골라 최소한의 public API를 설계하자. 그 오이에는 클래스, 인터페이스, 맴버가 의도치 않게 API로 공개되는 일이 없도록 해야 한다. public 클래스는 상수용 public static final 필드 외에는 어떠한 public 필드도 가져서는 안 된다. public static final 필드가 참조하는 객체가 불변인지 확인하라!

 

1. 아이템 개념

  • 클래스와 맴버의 접근권한을 최소화 하자!
  • 접근권한 종류 
    • private: 맴버를 선언한 톱 레벨 클래스에서만 접근할 수 있다.
    • package-private(default): 맴버가 소속된 패키지 안의 모든 클래스에서 접근할 수 있다. 접근 제한자를 명시하지 않았을 때 적용되는 패키지 접근 수준이다 (단, 인터페이스의 맴버는 기본적으로 public이 적용된다).
    • protected: package-private의 접근 범위를 포함하며, 이 맴버를 선언한 클래스의 하위 클래스에서도 접근할 수 있다(제약이 조금 따른다).
    • public: 모든 곳에서 접근할 수 있다.

2. 필요한 이유

  • 접근 제어자를 사용하는 이유
    • 객체를 사용하는 입장에서 객체 내부적으로 사용하는 변수나 메소드에 접근함으로써 개발자가 의도하지 못한 오동작을 일으키기도 한다. 이러한 문제로부터 객체의 로직을 보호하기 위해서는 맴버에 따라서 외부의 접근을 허용하거나 차단해야 할 필요가 생긴다.
    • 사용자에게 객체를 조작 할 수 있는 수단만을 제공함으로서 결과적으로 객체의 사용에 집중 할 수 있도록 돕기 위함이다.

3. 아이템 개념의 반대 개념이나 관련 개념

  • final
    • Java에서 final 키워드는 배열이나 Coolections 객체 원소의 불변을 지켜 주진 못하지만, 그것들 앞에 붙임으로서 해당 API 개발자들에게 불변을 특히 신경쓸 것을 경고하는 방식으로 사용된다.
    • 변수의 final
      • 이 변수는 수정할 수 없다. 고로 초기화 값이 필수이다. 객체안의 변수라면 생성자, static 블럭을 통한 초기화까지는 허용한다.
      • 참조형 변수는 그 객체 내부의 값은 변경할 수 있지만 가리키는 객체는 변경하지 못한다.
    • 메서드의 final
      • 메서드에 final을 붙이면 overried를 제한한다. 상속 받은 클래스에서 해당 메서드를 수정해서 사용하지 못하도록 할 수 있는것이 메서드에 final을 붙이는 것이다. 상속받은 클래스의 메소드를 재정의 할때 사용되는 @Override 애노테이션을 붙이면 에러가 난다!
    • 클래스의 final
      • 클래스에 final을 붙이면 상속 불가능 클래스가 된다. 즉, 다른 클래스에서 상속하여 재정의를 할 수 없다.
  • package-private(default)

현업에서는 default를 잘 사용하지 않는다!
- default접근제한자를 잘 확용하는 경우가 분명히 존재하긴 하나, 대부분의 경우 default를 제대로 활용하기가 어려운 편이다. 특히 규모가 계속해서 커지는 프로젝트의 경우, 패키지 구조가 변경될 때마다 접근제한자가 발목을 잡을 가능성이 매우 높다. 

public class ValueTest {

  public void variableFinal() {

    final int value = 321;
    final Person person = new Person("리마김", 29);

    System.out.println("value = " + value);
    System.out.println("lima 1 = " + person);

    // value = 123; // 컴파일 에러
    person.setAge(10); // 객체 내부 값은 변경 가능
    person.setName("김리마");

    System.out.println("lima 2 = " + person);
    // person = new Person("헬로우", 29); // 컴파일 에러
  }
}

 

 

 

 

https://sabarada.tistory.com/148

 

[Java] final 키워드에 대해서 알아보자

안녕하세요. 오늘은 여러분들과 java의 final 키워드에 대해서 알아보려고 합니다. final final 키워드는 변수(variable), 메서드(method), 또는 클래스(class)에 사용될 수 있습니다. 이 final 키워드는 어떤

sabarada.tistory.com

https://hyeon9mak.github.io/Java-dont-use-package-private/

 

Java package-private 은 안쓰나요?

 

hyeon9mak.github.io

https://edu.goorm.io/learn/lecture/41/%EB%B0%94%EB%A1%9C%EC%8B%A4%EC%8A%B5-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9-%EC%9E%90%EB%B0%94-java/lesson/39214/%EC%A0%91%EA%B7%BC-%EC%A0%9C%EC%96%B4%EC%9E%90%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0

 

구름EDU - 모두를 위한 맞춤형 IT교육

구름EDU는 모두를 위한 맞춤형 IT교육 플랫폼입니다. 개인/학교/기업 및 기관 별 최적화된 IT교육 솔루션을 경험해보세요. 기초부터 실무 프로그래밍 교육, 전국 초중고/대학교 온라인 강의, 기업/

edu.goorm.io

https://velog.io/@max9106/Java-%EB%B0%A9%EC%96%B4%EC%A0%81-%EB%B3%B5%EC%82%ACDefensive-copy

 

[Java] 방어적 복사(Defensive copy)

생성자를 통해 초기화 할 때, 새로운 객체로 감싸서 복사해주는 방법이다.만약 생성자에서 유효성 검증이 필요하다고 하자. 일반적으로는 아래와 같이 넘어온 값을 검증하고 객체 내부변수에

velog.io

 

 

 

 

 

** 그냥 하루하루 개인 공부한 것을 끄적 거리는 공간입니다.

이곳 저곳에서 구글링한 것과 강의 들은내용이 정리가 되었습니다.

그림들은 그림밑에 출처표시를 해놓았습니다.

문제가 될시 말씀해주시면 해당 부분은 삭제 하도록하겠습니다. **

반응형

'public void static main() > Book' 카테고리의 다른 글

[Effective Java] 아이템 5  (0) 2023.01.24
[Effective Java] 아이템 17  (0) 2023.01.18
[Effective Java] 아이템 13  (0) 2023.01.10
[Effective Java] 아이템 10 ~ 아이템 11  (0) 2023.01.10
[Effective Java] 아이템 3  (0) 2023.01.03

댓글