언어/Effective Java

이펙티브 자바 Item 16 - public 클래스에서는 public필드가 아닌 접근자 메서드를 사용하라

now0204 2024. 5. 30. 09:52

 

1. 접근자 메서드를 활용한 데이터 캡슐화 

  • 대표적인 클래스 작성 실수: 인스턴스 필드만을 모아놓은 퇴보한 클래스 
  • 인스턴스 필드만을 모아놓은 클래스는 데이터 직접 접근할 수 있으나 캡슐화의 이점을 제공하지 못한다.
public class Point {
    public double x;
    public double y;
}

 

  • 캡슐화가 전혀 안되기 때문에 아래와 같은 단점을 수반한다. 
    • API를 수정하지 않고는 내부 표현을 바꿀 수 없다
    • 불변식을 보장하지 못한다.
    • 외부에서 필드에 접근할 때 부수작업을 수행할 수 없다.

*API 수정부분 

Point.getValue() 같은 메서드를 호출해서 x,y를 얻는다고 했을 때 내부 구현 로직이 캡슐화되어 있기 때문에 얼마든지 바꿀 수 있음 

Point.x로 접근했을 때, 만약 x의 이름이 xy로 바뀐다면, Point.x를 사용하는 모든 클래스를 수정해줘야할 것임

 


2. 클래스 캡슐화 

 

class Point {

    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() { return x; }
    public double getY() { return y; }

    public void setX(double x) { this.x = x; }
    public void setY(double y) { this.y = y; }

}
  • getter/setter 메서드를 통해 언제든지 내부 표현을 바꿀 수 있다.
  • 불변식이 보장된다.
    • 클라이언트는 메서드를 통해서만 필드에 접근 가능하다.
  • 외부에서 필드에 접근할 때 부수 작업을 수행 시킬 수 있다.
    • getter/setter 메서드에 얼마든지 추가 가능하다.

객체지향의 장점을 이끌어내려면, 아무리 단순한 데이터(필드)라도 이처럼 캡슐화를 진행하는 것이 좋다.

 

 2.1 private 중첩 클래스

  • 하지만 pakage-private 혹은 private 중첩 클래스라면 데이터 필드를 노출한다 해도 문제가 없다
public class TopPoint {

    private static class Point {
        public double x;
        public double y;
    }

    public Point getPoint() {
        Point point = new Point();  // TopPoint 외부에선
        point.x = 3.5;              // Point 클래스 내부 조작이
        point.y = 4.5;              // 불가능 하다.
        return point;
    }

}
  • 위와 같이 private 클래스를 중첩시키면 TopPoint 클래스에서는 얼마든지 Point클래스의 필드를 조작할 수 있지마느 외부 클래스에서는 Point 클래스의 필드에 직접 접근 할 수 없다.

 

  • package-private 클래스 역시 해당 클래스가 포함되는 패키지 내에서만 조작이 가능하고 패키지 외부에서는 접근이 불가능 하므로, 처음 제시된 3가지 문제점을 모두 메꿀 수 있다.

 

  • point.x로 접근해서 사용하면 훨씬 사용이 편리하긴 하다. 이런 장점을 살리면서 캡슐화의 이점도 가져간다.

*public class에서 public 필드를 만들면 공개 API라서 클라이언트가 전 세계이다. 이는 수정이 매우 어렵다.

하지만, package-private나 private 중첩 클래스에 public은 수정 범위가 패키지 내부나 클래스 내부이므로, 비교적 수정이 용이하다.

 

 

public을 노출하되, 불편 필드로 만들기 

public class Time {
    private static final int HOURS_PER_DAY = 24;
    private static final int MINUTES_PER_HOUR = 60;
    
    public final int hour;
    public final int minute;

    public Time(final int hour, final int minute) {
        this.hour = hour;
        this.minute = minute;
    }   
}

 

  • public 클래스 필드가 불변이라면 직접 노출할 때의 단점이 조금 줄어든다.
  • 다만 API를 변경하지 않고는 표현 방식을 바꿀 수 없고, 필드를 읽을 때 부수적인 작업을 수행할 수 없다는 단점이 변하지 않는다.