객체지향 생활체조 원칙은 소트웍스 앤솔러지(ThoughtWorks Anthology) 라는 책에 나오는 원칙이다.

 

목차

  1. 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.
  2. else 예약어를 사용하지 않는다.
  3. 모든 원시 값과 문자열을 포장한다.
  4. 일급 컬렉션을 쓴다.
  5. 한 줄에 점을 하나만 찍는다.
  6. 줄여 쓰지 않는다 ( 축약 금지 )
  7. 모든 엔티티를 작게 유지한다.
  8. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
  9. Getter / Setter / Property를 쓰지 않는다.

 

 

Getter / Setter / Property를 쓰지 않는다.


벌써 마지막 9번째 입니다. 여전히 마지막까지 추상적인 말만 늘어놓네요.

이 규칙은 정말로 getter setter를 쓰지말라는 의미가 아닙니다. 객체 내부에 어떤 속성이 있는지 외부에서 알지 못하게 하는 캡슐화에 초점을 맞춰야합니다.

 

TDA 원칙이라는것이 있습니다. Tell, Don't ask, 즉 정보를 묻지말고 하고싶은걸 하라는건데요.

 

 

 

예시로 값이 짝수인지 확인하는 로직을 만들어보겠습니다.

public class Number {

    private int number;

    public Number(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }
    
}

public class Main {

    public static void main(String[] args) {

        Number number = new Number(5);
        int intNumber = number.getNumber();

        if (intNumber%2 == 0) {
            System.out.println("짝수임");
        } else {
            System.out.println("짝수아님");
        }
    }
}

 

number.getNumber()를 호출하면서 객체 내부의 값이 노출되었고, 그 값을 로직에 사용했습니다. 확실히 캡슐화가 깨졌습니다.

짝수인지 판단하는 로직을 Number 객체 안에 숨겨보겠습니다.

 

 

public class Number {

    private final int number;

    public Number(int number) {
        this.number = number;
    }

    public void isEven() {
        if (number%2 == 0) {
            System.out.println("짝수임");
        } else {
            System.out.println("짝수아님");
        }
    }
}

public class Main {

    public static void main(String[] args) {

        Number number = new Number(5);
        number.isEven();

    }
}

 

Getter가 사라져 Number 객체안에 값을 알지 못하게되었습니다.

 

그렇다면 Gette는 절대 사용하면 안되는것일까요? 당연히 아닙니다. 경우에 따라 다릅니다.

객체의 값을 외부로 표현해주어야할 경우에는 Getter를 사용해야합니다.

 

public void printNumber() {
    System.out.println("현재 값은 " + number + " 입니다.");
}

 

 

 

 

 

Setter의 경우도 마찬가지입니다.

 

public class Main {

    public static void main(String[] args) {

        Number number = new Number(5);
        number.setNumber(number.getNumber() + 2);

    }
}

현재값에 2를 더하는 로직입니다. setter를 사용하기 위해 getter + 2를 하는건 상당히 보기 안좋습니다.

 

public void addNumber(int number) {
    this.number += number;
}


public class Main {

    public static void main(String[] args) {

        Number number = new Number(5);
        number.addNumber(2);

    }
}

 

핵심은 캡슐화라는것을 명심해주세요.

객체지향 생활체조 원칙은 소트웍스 앤솔러지(ThoughtWorks Anthology) 라는 책에 나오는 원칙이다.

목차

  1. 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.
  2. else 예약어를 사용하지 않는다.
  3. 모든 원시 값과 문자열을 포장한다.
  4. 일급 컬렉션을 쓴다.
  5. 한 줄에 점을 하나만 찍는다.
  6. 줄여 쓰지 않는다 ( 축약 금지 )
  7. 모든 엔티티를 작게 유지한다.
  8. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
  9. Getter / Setter / Property를 쓰지 않는다.

 

모든 원시 값과 문자열을 포장한다.

 

쉽게말해 int, String 과 같은 타입의 값을 객체로 포장하는겁니다.

 

public class Student {

    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

 

Student 객체에는 이름과 나이를 가지고 있습니다.

 

만약 이름은 최소 2자 이상, 나이는 20세 이상 이라는 조건이 추가된다면?

 

public class Student {

    private String name;
    private int age;

    public Student(String name, int age) {
        validName(name);
        validAge(age);
        this.name = name;
        this.age = age;
    }

    private void validName(String name) {
        if (name.length() < 2) {
            throw new IllegalStudentNameException("이름은 최소 2자 이상이어야 합니다.");
        }
    }
    private void validAge(int age) {
        if (age < 20) {
            throw new IllegalStudentAgeException("나이는 20살 이상이어야 합니다.");
        }
    }

}

 

잘못된 값의 검증을 제외한 일반적인 예외처리를 했음에도 Student가 할 일이 엄청 늘어났습니다.

Student는 이제 이름과 나이에 대한 상태관리를 모두 해야합니다.

그럼 만약 Student에 이메일, 성별 등 변수가 추가된다면 코드는 복잡해질것입니다.

 

이제 원시 타입 변수로 포장해보겠습니다.

public class Student {

    private Name name;
    private Age age;

    public Student(Name name, Age age) {
        this.name = name;
        this.age = age;
    }
}

public class Name {

    private String name;

    public Name(String name) {
        validName(name);
        this.name = name;
    }

    private void validName(String name) {
        if (name.length() < 2) {
            throw new IllegalStudentNameException("이름은 최소 2자 이상이어야 합니다.");
        }
    }

}

public class Age {

    private int age;

    public Age(int age) {
        validAge(age);
        this.age = age;
    }

    private void validAge(int age) {
        if (age < 20) {
            throw new IllegalStudentAgeException("나이는 20살 이상이어야 합니다.");
        }
    }

}

 

이제 Student에서 이름과 나이에 대한 책임이 분리 되면서 단일 책임 원칙(SRP)도 지키게 되었습니다.

+ Recent posts