본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
7장 : 클래스와 객체

클래스와 객체의 책임

클래스는 관련된 데이터와 동작을 하나의 타입으로 정의하고 객체는 그 설계로 만들어진 개별 상태입니다.

단순히 필드와 메서드를 한 파일에 모았다고 좋은 객체가 되는 것은 아닙니다.

객체가 스스로 지켜야 하는 규칙과 외부에 제공할 동작을 함께 정해야 합니다.

생성된 두 객체는 같은 클래스를 사용해도 서로 다른 필드 값을 가질 수 있습니다.

이 절에서는 데이터 소유자와 동작 책임자를 같은 경계에 두는 기준을 살펴봅니다.


클래스는 가능한 상태와 동작을 정의한다

필드는 객체가 기억할 상태이고 메서드는 그 상태를 읽거나 바꾸는 규칙입니다.

외부 코드가 필드를 직접 조합해야 한다면 책임이 밖으로 새어 나갑니다.


객체마다 인스턴스 상태가 따로 있다

new를 두 번 호출하면 같은 클래스의 서로 다른 객체가 만들어집니다.

한 객체의 필드를 바꿔도 다른 객체에는 영향을 주지 않습니다.

같은 클래스로 객체 두 개를 만들고 한쪽 잔액만 바꿔 봅니다. 클래스는 가능한 상태와 동작을 한 번 정의하지만, 인스턴스 필드는 생성된 객체마다 따로 존재한다는 사실이 출력에 드러납니다.

AccountObjects.java
public class AccountObjects {
    static final class Account {
        private final String owner;
        private int balance;

        Account(String owner) { this.owner = owner; }

        void deposit(int amount) {
            if (amount <= 0) throw new IllegalArgumentException("amount");
            balance += amount;
        }

        String summary() { return owner + ":" + balance; }
    }

    public static void main(String[] args) {
        Account ada = new Account("Ada");
        Account linus = new Account("Linus");
        ada.deposit(100);
        linus.deposit(40);
        System.out.println(ada.summary());
        System.out.println(linus.summary());
    }
}

첫 번째 객체의 입금이 두 번째 객체에 영향을 주지 않아야 정상입니다. 둘이 함께 바뀐다면 인스턴스 상태여야 할 필드를 static으로 두었거나 같은 가변 객체를 공유하는지 확인합니다.

Ada:100
Linus:40

같은 Account 클래스로 만든 두 객체가 각자의 balance를 독립적으로 보관합니다.


참조 변수와 객체를 구분한다

변수에는 객체 위치를 나타내는 참조가 저장됩니다.

두 변수가 같은 객체를 가리킬 수 있고 변수 하나가 null일 수도 있습니다.

참조 변수에는 객체 자체가 아니라 객체를 찾는 값이 들어갑니다. 두 참조가 같은 객체를 가리킬 수도 있고 참조가 null일 수도 있으므로, 변수 선언과 객체 생성은 서로 다른 사건입니다.

Account first = new Account("A");
Account alias = first;
alias.deposit(10); // 두 변수가 같은 객체를 가리킴

Account absent = null;
// absent.deposit(10); // 실행 시 NullPointerException

// first.balance = -100; // 컴파일 오류: private 필드

행동 중심으로 공개 API를 고른다

잔액 필드를 공개하기보다 deposit, withdraw처럼 유효성 규칙을 포함한 동작을 제공합니다.

객체가 자신의 불변식을 한곳에서 지킬 수 있습니다.

필드를 그대로 공개하면 호출자가 검증 없이 상태를 만들 수 있습니다. deposit, withdraw처럼 의도가 있는 동작을 열어 두면 음수 금액 거부나 잔액 부족 규칙을 객체 한곳에서 지킬 수 있습니다.


클래스를 설계할 때는 어떤 데이터를 갖는가보다 그 데이터의 유효성을 누가 책임지는가를 먼저 정해야 합니다.