- 구현과 API를 분리하는 "정보 은닉"의 장점
- 시스템 개발 속도를 높인다. (여러 컴포넌트를 병렬로 개발할 수 있기 때문에)
- 시스템 관리 비용을 낮춘다. (컴포넌트를 더 빨리 파악할 수 있기 때문에)
- 성능 최적화 도움을 준다. (프로파일링을 통해 최적화할 컴포넌트를 찾고 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 개선할 수 있기 때문에)
- 소프트웨어 재사용성을 높인다. (독자적인 컴포넌트라면)
- 시스템 개발 난이도를 낮춘다. (전체를 만들기 전에 개별 컴포넌트를 검증할 수 있기 때문에)
- 클래스와 인터페이스의 접근 제한자 사용 원칙
- 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.
- 톱레벨 클래스(inner class가 아닌 가장 밖에 있는 class)와 인터페이스에 package-private(default) 또는 public을 쓸 수 있다.
- public으로 선언하면 API가 되므로 하위 호환성을 유지하려면 영원히 관리해야한다.
- 패키지 외부에서 쓰지 않을 클래스나 인터페이스라면 package-private로 선언한다.
- 한 클래스에서만 사용하는 package-private 클래스나 인터페이스는 해당 클래스에 private static으로 중첩 시키자.
- 멤버(필드, 메서드, 중첩 클래스/인터페이스)의 접근 제어자 원칙
- private와 package-private는 내부 구현
- public 클래스의 protected와 public은 공개 API
- 코드를 테스트하는 목적으로 private를 package-private로 풀어주는 것은 허용할 수 있다. 하지만 테스트만을 위해서 멤버를 공개 API로 만들어서는 안 된다. (테스트를 같은 패키지에 만든다면 그럴 필요도 없다.)
- public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.
- 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안 된다.
MemberService 인터페이스는 public, package-private 접근제한자 중 어떤것을 사용하는 것이 맞을까?
보통 인터페이스는 기능 설계도이지만 외부에 기능 공유 목적으로 사용하는 API이기 때문에 public 접근 제한자를 사용하고 해당 서비스를 구현한 구현체는 패키지 내부에서 기능을 처리하기 때문에 package-private로 접근제한자를 설정한다.
MemberService를 구현한 DefaultMemberService 클래스는 내부 패키지 안에서만 어떤 일을 하는지 알면되지 외부에서 어떤 일을 하는지 자세하게 알 필요가 없다.
가져다 쓰는 MemberService 인터페이스만 알고 컨테이너에서 제공되는 기능으로 사용하면 된다.
도메인인 Member 클래스는 데이터를 가지고 여기저기 왔다 갔다 할 수 있기 때문에 public 접근 제한자를 사용.
MemberRepository 인터페이스의 경우 인터페이스이지만 서비스에서 데이터베이스를 거쳐가는 곳이기 때문에 외부에서 접근해 사용할 일이 없다면 내부 구현인 package-private 접근제어자를 갖는다.
MemberService를 구현한 DefaultMemberService에서만 사용하는 pacage-private 톱레벨 인터페이스일 경우 별도의 인터페이스가 아닌 DefaultMemberService 클래스 안에 private static으로 중첩시켜 사용하는 것이 좋을 수 있다.
톱레벨(별도의 인터페이스)로 두면 같은 패키지 모든 클래스가 접근할 수 있지만 private static으로 중첩 시키면 중첩하고 있는 클래스에서만 접근할 수 있다.
물론 패키지 내부 다른 서비스 구현체들에 여러번 사용된다면 톱레벨로 두고 패키지 내부 구현인터페이스로 사용한다.
static으로 선언한 MemberRepository 클래스는 실행되면서 static 메모리 영역에 별도의 인스턴스가 생성된 참조값을 갖게 되므로 독립된 객체로 private 접근제어자가 붙었기 때문에 DefaultMemberService 클래스에서만 사용 가능하다. PrivateClass 중첩 클래스(inner class)는 DefaultMemberService 인스턴스의 참조와 유효 범위 안의 지역 변수 값들을 저장하기 위해 컴파일러가 생성한 필드들이 자동 추가되어 MemberRepository 사용이 가능하다.
클래스 인스턴스의 필드는 가능한 public 사용금지.
클래스의 공개 API를 제외하고 모두 private 접근제어자를 사용하고 같은 패키지 다른 클래스가 접근해야 하는 멤버는 package-private로 권한을 풀어준다. protected 접근 제어자는 상속을 받은 클래스에서 사용할 수 있어서 공개 API이므로 설계에 신중해야 한다.
단지 코드를 테스트 하려는 목적으로 클래스, 인터페이스, 멤버의 접근범위를 넓히려고 할 떄가 있는데 public 클래스의 private 멤버를 package-private 까지 풀어주는 것은 허용할 수 있지만 그 이상은 안된다. 테스트만을위해 공개 API로 만들어서는 안된다. 테스트 패키지에서 패키지 경로가 같다면 접근 제어자가 package-private 이상이면 사용 가능하다. 테스트에서 필요할 경우 @Mock 객체를 사용하는 방법도 있다.
상수의 경우에는 public static final로 기본타입값을 가지거나 불변 객체를 참조한다면 public으로 선언해도 되지만 가변객체를 갖는다면 스레드에 안전하지 않다.
상수라도 배열 필드라면 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다. 클라이언트에서 배열의 내용을 수정할 수 있게 된다.
Serializable을 구현한 클래스들은 private, package-private 인스턴스 필드들이 공개 API가 될 수 있다.
직렬화 할 때 해당 필드들은 직렬화 형태에 포함되는 공개 API에 속한다.??????
상위 클래스의 메서드를 재정의 할 때는 접근 수준을 상위 클래스보다 좁게 설정할 수 없다.
이 제약은 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다는 리스코프 치환 원칙을 지키기 위해 필요하다. 만약 상위 클래스에서 protected로 선언된 메서드를 오버라이딩 할 때 package-private나 private로 선언할 시 컴파일 오류가 난다. 같은 protected나 public으로 선언해야 한다.
public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않은 이유는 멀티 스레드 환경에서 어떤 스레드가 언제 가변 필드를 변경할지 알 수 없기 때문 A 스레드가 name이라는 가변 필드의 값을 설정하고 업무를 처리할 때 B 스레드에서 name 값을 변경한다면 A 스레드가 원하는 값으로 처리가 안될 가능성이 있기 때문
자바 9 모듈 (JPMS Java Platform Module System)
https://live-everyday.tistory.com/208
정보은닉 (information hiding)이란 다른 객체에게 자신의 정보를 숨기고 자신의 연산만을 통해 접근을 허용하는 것으로, 클래스 외부에서 특정 정보에 접근을 막는다는 의미이다.
정보은닉은 모든 객체지향 언어적 요소를 활용하여 객체에 대한 구체적인 정보를 노출시키지 않도록 하는 기법으로 객체지향프로그램밍에서 중요한 개념이다. 정보은닉의 개념은 캡슐화와 유사한 부분이 많아 같은 개념이라고 많이들 오해하지만 시스템 처리 원리를 기준으로 본다면 다른 개념이다.
정보은닉의 목적
- 기능의 교체나 변경에 대한 유연성을 제공하는 것이다. 코드가 타입이나 메소드, 구현등에 의존하는 것을 막아줌으로써 객체 간의 구체적인 결합도를 약화시킨다. 결합도가 약화되면 기능을 변경하거나 다른 기능으로 교체하는게 쉬워진다.
- 동일한 타입의 다른 구현 객체들을 교체함으로써 동적 기능 변경이 가능하다.
- 연동할 구체적인 구현이 없는 상태 즉, 인터페이스만으로도 정확한 연동 코드의 생성이 가능하다.
- 코드를 적절히 모듈화하여 주어진 코드의 일부를 이해하기 위해 필요한 정보의 양이 적기 때문에 코드를 이해하기가 더 쉽다.
- 상호 교환이 필요한 정보의 양을 최소화하면서 독자적으로 각 모듈에 대한 작업을 진행할 수 있기에 개발 기간을 단축시킬 수 있다.
캡슐화를 통한 정보은닉(Information hiding)의 장점
- 객체에 포함된 정보의 손상과 오용을 막을 수 있다. 정보은닉은 단어 자체로 정보를 숨기는 것이기 때문에 권한이 없는 외부의 객체가 접근할 수 없어 정보 손상이 발생하지않는다.
- 객체의 사용방법은 바뀌지않는다. 정보은닉된 객체의 인터페이스만으로 접근하기 때문에 내부 조작 방법이 바뀌어도 접근방법이 변경되진않는다.
- 데이터가 바뀌어도 다른 객체에 영향을 주지않아서 독립성이 유지된다.
- 객체의 이식성이 좋다. 객체의 정보에 직접적인 접근이 아닌, 연산만을 통한 접근이다. 그렇기에 연산으로 출력된 결과물만 사용하므로써 객체의 이식성이 높아진다.
- 객체를 시스템 구성에서 부품처럼 사용할 수 있다. 연산으로 인해 출력된 결과물만 사용하기 때문에 객체를 부품화 할 수 있다. 부품화된 객체를 새로운 시스템 구성에서 부품처럼 사용이 가능하다.
정보은닉(Information hiding)과 캡슐화(Encapsulation)는 개념에 유사한 부분이 많긴하지만 다른 개념이다.
캡슐화는 데이터를 공용 메소드를 통해서만 접근할 수 있도록 허용하는 방법이다. 데이터 캡슐화라고도 하는데 정보 은닉은 데이터 캡슐화가 아닌 외부에 감춰야하는 비밀에 따라 시스템을 분할하는 모듈 분할의 원리이다.
모듈은 변경될 가능성이 있는 비밀을 내부로 감추고 잘 정의되고 쉽게 변경되지않는 공용 인터페이스를 외부에 제공하여 내부의 비밀에 함부로 접근할 수 없도록 하는 것이다.
캡슐화는 관련된 요소들을 묶음으로써 캡슐 내부와 외부를 구별 짓기 때문에 캡슐 내에 속한 부분과 캡슐 외에 속한 부분들에 대해 구분이 명확하게 되있다. 즉, 특정 객체 속에 있는 데이터와 함수들을 다른 객체 속에 있는 데이터와 함수들과 구별이 이뤄진다는 의미이다.
하지만 정보은닉은 캡슐 내의 요소들에 대한 세부 구현사항들을 외부로부터 숨기는 것이다. 즉, 캡슐화가 되어있는 데이터와 함수에 대해서 외부에 해당 함수가 어떻게 구현되어 있는지에 대한 세부 사항을 숨기는 것이다. 정보은닉의 개념이 캡슐화 개념안에 포함되긴 하지만, 캡슐화가 되어있다고 해서 반드시 정보은닉이 되는 것은 아니다.
캡슐화(capsule化) 또는 인캡슐레이션(encapsulation)이란 객체 지향 프로그래밍(OOP)에서 객체의 데이터와 기능을 하나로 묶고 외부에 노출되지 않도록 숨김 처리하는 것을 말한다. 변수, 메소드, 클래스에 대해 접근 제어자를 사용함으로써 캡슐화를 할 수 있다.
캡슐화는 외부로부터 데이터를 보호하기 위해 외부에는 불필요하거나 내부적으로만 사용되는 부분을 감추기 위해 특정 속성이나 메소드를 사용자가 사용할 수 없도록 은닉화한 것이다. 캡슐화는 객체의 필드(속성), 메소드를 하나로 묶어 실제 구현 내용을 외부에 감추는 것을 말한다. 외부 객체는 객체 내부의 구조를 얻지 못하며 객체가 제공하는 필드와 메소드만 이용할 수 있다. 필드와 메소드를 캡슐화하여 보호하는 이유는 외부의 잘못된 사용으로 인한 객체의 손상을 방지하기 위함이다. 자바에서는 캡슐화된 멤버를 접근제어자를 사용하여 노출시킬지 숨길지 결정할 수 있다.
접근 제어자는 멤버 또는 클래스에 사용되어 외부에서 접근하지 못하도록 제한하는 역할을 한다. 접근제어자는 생략할 수 있으며 생략 시 자동으로 디폴트(default)로 지정된다. 접근제어자가 사용될 수 있는 곳은 클래스, 멤버변수, 메소드, 생성자이다.
- 퍼블릭(public) : 접근 제한이 없다. 다른 패키지, 다른 클래스에서 접근할 수 있다.
- 프로텍티드(protected) : 같은 패키지 내에서 접근할 수 있다. 다른 클래스에서는 접근할 수 없지만, 상속 받은 자식 클래스에서는 접근이 가능하다.
- 디폴트(default, package-private) : 같은 패키지내에서만 접근이 가능하다.
- 프라이빗(private) : 같은 클래스 내부에서만 접근할 수 있다.
객체 지향 프로그래밍에서 객체의 데이터는 객체의 외부에서 직접적으로 접근하는 것을 막는다. 객체의 데이터를 외부에서 마음대로 읽고 변경할 경우 객체의 무결성(결점이 없는 성질)이 깨어질 수 있기 때문이다. private을 통해 외부로부터의 데이터 접근을 차단한 변수를 set Method와 get Method를 통해 데이터를 저장, 수정, 출력할 수 있다.
Getter
외부에서 객체의 데이터를 읽을 때 메소드를 사용한다. 객체 외부에서 객체의 필드 값을 사용할 때 부적절한 경우도 있는데 이런 경우 Getter 메소드로 필드값을 가공한 후 외부로 전달한다.
Setter
객체 지향 프로그래밍에서는 메소드를 통해 데이터를 변경하는 방법을 사용한다. Setter는 메소드의 매개값을 검증하여 유효한 값만 데이터로 저장할 수 있기 때문에 데이터는 외부에서 접근할 수 없도록 막고 메소드는 공개해서 외부에서 메소드를 통해 데이터에 접근할 수 있도록 유도한다.
'개인룸 > 도윤' 카테고리의 다른 글
Item17. 변경가능성을 최소화 하라 (0) | 2023.02.27 |
---|---|
Item16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2023.02.23 |
Item.14 Comparable을 구현할지 고려하라 (0) | 2023.02.12 |
Item.13 clone 재정의는 주의해서 진행하라. (0) | 2023.02.05 |
Item12. toString을 항상 재정의하라 (0) | 2023.02.04 |