Item16. Accessor Methods Over Public Fields

Public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

자바에서의 클래스 설계 시, 객체 지향 원칙을 준수하는 것이 중요합니다. 이 원칙 중 하나는 클래스의 상태를 외부에 노출하지 않고, 접근자 메서드를 통해 접근하게 하는 것입니다. 이러한 설계는 클래스의 유연성을 높이고, 유지보수성을 향상시킵니다.

1. Public 필드의 문제점

클래스의 필드를 public으로 선언하게 되면, 그 필드에 직접 접근이 가능합니다. 이 방법은 코드 작성이 간단하고, 초기 학습 단계에서는 매력적으로 보일 수 있습니다. 하지만, 이 방식에는 다음과 같은 문제점들이 존재합니다:

  1. 캡슐화의 위반: 객체 지향 프로그래밍에서 중요한 원칙 중 하나인 캡슐화(encapsulation)는 객체의 상태를 외부에서 직접 변경하지 못하도록 보호하는 것입니다. 그러나 필드가 public으로 선언되면, 외부에서 직접 해당 필드를 조작할 수 있어 객체의 상태가 불안정해질 수 있습니다. 이로 인해 버그가 발생할 가능성이 높아집니다.

  2. 변경에 취약: public 필드를 사용하는 경우, 해당 필드를 사용하는 모든 코드가 그 필드의 내부 구조에 의존하게 됩니다. 이로 인해 필드의 타입이나 의미를 변경하려면 그 필드를 참조하는 모든 코드를 수정해야 합니다. 반면, 접근자 메서드를 사용하면 메서드 내부에서 필드에 대한 변경 사항을 감출 수 있습니다.

  3. 불변성을 보장하기 어려움: 불변 객체는 다중 스레드 환경에서 안전하게 사용할 수 있어 자바에서 중요한 개념입니다. 하지만 public 필드를 사용하면 객체의 불변성을 보장하기 어렵습니다. 필드를 public으로 노출하면, 외부에서 해당 필드를 변경할 수 있기 때문에, 객체의 상태가 변하게 됩니다.

예시: Public 필드 사용의 문제점
public class Point {
    public int x;
    public int y;
}

Point p = new Point();
p.x = 5;
p.y = 10;

위의 코드는 직관적이고 간단해 보일 수 있지만, 문제의 소지가 있습니다. xy의 값을 아무나 변경할 수 있으며, 이러한 변경이 예상치 못한 버그를 일으킬 수 있습니다.


2. 접근자 메서드를 사용해야 하는 이유

접근자 메서드를 사용하면, 객체의 내부 구현을 숨기고 외부와의 상호작용을 제어할 수 있습니다. 다음은 접근자 메서드를 사용해야 하는 몇 가지 주요 이유입니다:

  1. 캡슐화: 필드를 private으로 선언하고, 접근자(getter)와 설정자(setter) 메서드를 제공하면, 클래스 외부에서는 이 필드에 직접 접근할 수 없습니다. 이로 인해 클래스의 내부 구현이 외부로부터 보호됩니다.

  2. 유연성: 접근자 메서드를 사용하면, 필드의 값이 읽히거나 쓰일 때 추가적인 로직을 삽입할 수 있습니다. 예를 들어, 값을 읽을 때 캐싱을 사용하거나, 값을 설정할 때 유효성 검사를 추가할 수 있습니다.

  3. 불변성 강화: 클래스의 필드를 final로 선언하고, 생성자에서만 초기화하게 하면, 해당 클래스는 불변 객체가 됩니다. 이는 다중 스레드 환경에서 안전성을 제공합니다.

  4. 인터페이스와의 일관성: 접근자 메서드를 사용하면, 클래스의 필드를 외부에 공개하지 않고도 인터페이스를 통해 일관된 방법으로 접근할 수 있습니다. 이는 코드의 가독성과 유지보수성을 높이는 데 도움이 됩니다.

예시: 접근자 메서드를 사용한 개선된 코드

위의 코드에서는 xy 필드가 private으로 선언되어 있으며, 이를 통해 캡슐화가 이루어집니다. 외부에서는 getX()getY() 메서드를 통해서만 xy의 값을 읽을 수 있습니다.


3. 불변 클래스의 설계

불변 클래스는 객체가 생성된 이후 그 상태를 변경할 수 없는 클래스를 의미합니다. 이러한 클래스는 다중 스레드 환경에서 안전하게 사용될 수 있어, 자바에서 특히 중요합니다. 불변 클래스를 설계할 때 접근자 메서드를 사용하는 것은 필수적입니다.

예시: 불변 클래스 설계

위의 ImmutablePoint 클래스는 불변 객체로, xy 필드는 final로 선언되어 있으며, 생성자를 통해 초기화됩니다. 필드를 외부에 노출하지 않고, 접근자 메서드를 통해서만 값을 읽을 수 있습니다. 이로써, 해당 클래스는 불변성을 보장하며, 안전하게 사용할 수 있습니다.


4. Lombok을 사용한 접근자 메서드 자동 생성

자바에서 접근자 메서드를 수동으로 작성하는 것은 반복적이고 번거로울 수 있습니다. 이럴 때, Lombok 라이브러리를 사용하면 접근자 메서드를 자동으로 생성할 수 있습니다. Lombok을 사용하면 코드가 간결해지고, 유지보수성이 향상됩니다.

Lombok 예시

위 코드에서 Lombok의 @Getter 애노테이션을 사용하여 xy 필드에 대한 접근자 메서드가 자동으로 생성됩니다. 이로 인해 코드를 더 간결하게 유지할 수 있습니다.

6. 캡슐화와 정보 은닉의 중요성

캡슐화(encapsulation)와 정보 은닉(information hiding)은 객체 지향 프로그래밍의 핵심 개념으로, 클래스 설계에서 중요한 역할을 합니다. 이 두 개념은 클래스 내부의 구현 세부 사항을 외부로부터 숨기고, 외부와의 상호작용을 제한함으로써 코드의 안정성과 유지보수성을 높이는 데 기여합니다.

public 필드를 사용하는 것은 캡슐화와 정보 은닉의 원칙을 위반하는 행위입니다. 반면, 접근자 메서드를 통해 필드에 접근하도록 설계하면, 클래스 내부의 구현을 보호할 수 있습니다.

캡슐화의 예시

캡슐화의 주요 목표는 객체의 내부 상태를 보호하는 것입니다. 예를 들어, 계좌의 잔액을 다루는 BankAccount 클래스가 있다고 가정해 봅시다.

위의 코드에서 balance 필드는 private으로 선언되어 있으며, 외부에서 직접 접근할 수 없습니다. 대신 deposit()withdraw() 메서드를 통해 잔액을 변경할 수 있습니다. 이렇게 하면 잘못된 값이 잔액으로 설정되는 것을 방지할 수 있습니다.

정보 은닉의 예시

정보 은닉은 캡슐화와 밀접한 관련이 있으며, 클래스 내부의 세부 구현을 외부에서 숨기는 것을 목표로 합니다. 예를 들어, TemperatureSensor 클래스가 온도를 측정하는 방식이 변경될 가능성이 있다고 가정해 봅시다.

이 클래스의 내부 구현이 변경되어 온도를 섭씨에서 화씨로 저장해야 한다고 해도, 외부 코드에는 영향을 주지 않습니다. 외부에서는 여전히 getTemperatureInCelsius() 메서드를 통해 섭씨 온도를 읽을 수 있습니다. 이러한 설계는 정보 은닉을 통해 코드의 유연성과 확장성을 높입니다.


7. 접근자 메서드의 유효성 검사 및 추가 로직

접근자 메서드를 사용하는 또 다른 중요한 이유는 필드 값에 대한 유효성 검사와 추가 로직을 적용할 수 있다는 점입니다. 필드에 직접 접근하면 이러한 로직을 적용할 수 있는 기회가 사라지지만, 접근자 메서드를 사용하면 필요에 따라 다양한 검사를 추가할 수 있습니다.

유효성 검사의 예시

아래의 예시는 유효성 검사를 통해 잘못된 데이터가 설정되는 것을 방지하는 예시입니다.

위 코드에서 setWidth()setHeight() 메서드는 폭과 높이가 0보다 큰지 확인합니다. 이러한 유효성 검사를 통해 잘못된 값이 필드에 설정되는 것을 방지할 수 있습니다.

추가 로직의 예시

setSalary() 메서드는 급여를 설정하면서 추가적으로 세금 정보를 업데이트하는 로직을 포함합니다. 이렇게 하면 필드 값의 변경과 관련된 모든 작업을 중앙 집중화할 수 있습니다.


8. 접근자 메서드와 객체 직렬화

객체 직렬화는 객체의 상태를 저장하거나 네트워크를 통해 전송할 때 사용됩니다. 자바에서는 Serializable 인터페이스를 사용하여 객체를 직렬화할 수 있습니다. 하지만, 직렬화 가능한 객체의 필드를 public으로 선언하는 것은 위험할 수 있습니다. 필드가 외부에 노출되면 객체의 내부 구현이 고정되어 버리기 때문에, 향후 변경이 어려워집니다.

접근자 메서드와 직렬화의 관계

접근자 메서드를 사용하면, 필드의 직렬화 여부를 제어할 수 있으며, 직렬화 과정에서 필드의 노출을 방지할 수 있습니다. 예를 들어, 민감한 데이터가 포함된 객체를 직렬화할 때는 이러한 필드를 transient로 선언하여 직렬화에서 제외할 수 있습니다.

위의 코드에서 password 필드는 transient 키워드를 사용하여 직렬화에서 제외되었습니다. 이를 통해 민감한 정보가 직렬화될 때 유출되는 것을 방지할 수 있습니다.


9. 접근자 메서드의 성능 고려사항

일부 개발자들은 성능상의 이유로 필드를 public으로 선언하기도 합니다. 접근자 메서드를 호출하는 것보다 필드에 직접 접근하는 것이 더 빠르다고 생각할 수 있기 때문입니다. 하지만, 현대의 자바 컴파일러와 JIT(Just-In-Time) 컴파일러는 이러한 차이를 최소화하며, 실제로는 접근자 메서드를 사용하는 것이 더 안전하고 권장됩니다.

성능 최적화와 접근자 메서드

실제 애플리케이션에서 성능 문제가 발생하는 경우는 드뭅니다. 대부분의 경우, 접근자 메서드를 사용하는 것이 성능에 미치는 영향은 미미하며, 코드의 가독성과 유지보수성을 높이는 데 더 큰 이점을 제공합니다. 따라서 성능 최적화보다는 안전성과 유연성을 우선시하는 것이 좋습니다.

Last updated