[독서] 엘레강트 오브젝트 1장. 출생

wally·2022년 6월 4일
0

독서시리즈

목록 보기
1/10

1.1 -er 로 끝나는 이름을 사용하지 마세요.

토론하기 : http://goo.gl/Uy3wZ6

1. 클래스는 팩토리이다.

저자는 책에서 클래스를 객체의 팩토리로 설명하고 있다.

객체지향의 사실과 오해를 읽으면서 타입과 클래스의 차이에 대해 고민해 본적이 있고, Java 라는 언어가 특히 타입과 클래스를 구분하기 어려운 측면이 존재한다.

잠시 이에 대해 예시를 들어 소개하자면 클래스는 Vectors 이고 타입은 Vector<String>Vector<Integer>. 즉 2가지 타입의 인스턴스는 동일한 클래스의 인스턴스 이지만 타입이 다른 것을 확인 할 수 있다.(자바는 raw type 이 있어 혼동을 준다)

위 설명을 보면 클래스를 객체의 팩토리로 설명하는 것을 이해 할 수 있다.

  • 클래스라는 팩토리가 존재하고
  • 여러가지 인스턴스들이 다양한 타입을 가지면서 만들어 질 수 있다.
  • 이러한 팩토리는 클래스를 능동적인 관리자로 바라본다.
  • 클래스는 객체를 꺼내거나 반환할 수 있는 위치이며
  • 클래스를 저장소(storage unit) 또는 웨어하우스라 불러야 한다고 말한다.
  • 궁극적으로 클래스를 객체의 어머니라고 할 수 있다.

2. 클래스는 (what he does)가 아니고 (What he is) 에 기반해야 한다.

클래스는 단순히 기능에 기반해서 이름을 지어서는 안된다.
클래스는 협력을 하기 위해 책임을 가지고 메세지를 주고 받는다.
이러한 클래스는 단순히 공장의 기계가 아닌 스스로 자율성과 캡슐화를 지닌 하나의 능동적인 책임의 관리자로 바라보는 것이 바람직하다. 따라서 클래스의 이름은 관리자로써의 그 자체에 초점을 모아야 한다.

따라서 이름을 지을 때 클래스를 기능으로 바라보는 '-er' 접미사를 사용하지 말아라.

추가 : 토론하기에 들어가면 1장 부터 굉장히 의견이 분분하다.


1.2 생성자 하나를 주 생성자로 만드세요

토론하기 : http://goo.gl/brqhYS

(용어정리)주 생성자 : ctor

1. ctor

응집도가 놓고 견고한 클래스에는 적은 수의 메서드와 상대적으로 많은 수의 ctor 이 존재한다(객체는 행동기반 + 단일책임) - 많은 수의 ctor 은 유연성을 향상시킨다.

저자는 1개의 ctor 을 위치시키고 여러개의 부(secondary) ctor 이 주 ctor 을 호출하도록 만들라고 한다.

final class Cash {
    private final int cents;
    private final String currency;
    public Cash() { // secondary
        this(0);
    }
    public Cash(int cts) { // secondary
        this(cts, "USD");
    }
    public Cash(int cts, String crn) { // primary
        this.cents = cts;
        this.currency = crn;
    }
    // methods here
}

'하나의 주 ctor 가 다수의 부 ctor(one primary, many secondaty)' 원칙의 핵심은 중복 코드를 방지하고 설계를 더 간결하게 만들기 때문에 유지 보수성이 향상되는 점이다

  • this 에 할당하는 것을 최대한 주 ctor 로 미룬다.
  • 그 예로 유효성 검사 로직을 주 ctor 에 두기만 하면 된다.

2. 그럼 주 ctor 은 변수 할당밖에 기능이 없는건가?

맞다. 바로 그점이 포인트이다.


1.3 생성자에 코드를 넣지 마세요

토론하기 : http://goo.gl/DCMFDY

  • 주 ctor 은 객체 초기화 프로세스를 시작하는 유일한 장소이기 때문에 제공되는 인자들은 완전해야 한다. 어떤 것도 누락하지 않고, 중복 정보도 없어야 한다.
  • 객체 초기화에는 코드가 없어야 하고 인자를 건드려서는 안된다.
  • 대신 인자들을 다른 타입의 객체로 감싸거나 가공하지 않은 형식(raw form)으로 캡슐화해야 한다.

1. 코드 중복을 방지할 수 있다.

public final class EnglishName implements Name {
  private final String name;
  public EnglishName(final CharSequence text) {
    this.name = text.toString().split(" ", 2)[0];
  }
  @Override
  public String first() {
    return this.name;
  }
}
  • 위 코드는 초기화 마다 로직이 진행되고 first 메서드 호출 시 초기화 값을 반활할 뿐이다.
public final class EnglishName implements Name {
  private final CharSequence text;
  public EnglishName(final CharSequence txt) {
    this.text = txt;
  }
  @Override
  public String first() {
    return this.text.toString().split("", 2)[0];
  }
}
  • 다음과 같이 생상자에서 값을 받고, first 메서드가 사용되는 부분에서만 로직을 사용하는 것이 훨씬 효율적이다.
  • 데코레이터를 활용하는 것도 방법이다

2. 부 생성자에 로직이 있으면 안되지 않아??

class Cash {
  private int dollars;

  Cash(float dlr) {
    this((int) dlr);
  }

  Cash(String dlr) {
    this(Cash.parse(dlr));
  }

  Cash(int dlr){
    this.dollars = dlr;
  }
}
  • 1.2 에서 소개한 코드는 실제로 생성자 내부에 변환 로직이 있다.

3. 부 생성자에 로직이 있어도 괜찮을거 같은데??

  • 저자는 주 ctor 은 객체 초기화 프로세스를 시작하는 유일한 장소라고 하고 있다.
  • 그 말은 부 생성자는 객체 초기화 프로세스가 시작되는 부분이 아니다. 토론하기를 보면 parsed 를 통해 변환 메서드를 생성하라고 추천한다.
  • 하지만 pared 메서드는 클라이언트에게 보여줄 때 최종적으로 진행하는 메서드로 입력 받는 값의 타입 변경과는 무관하다.
  • 따라서 실제로 데이터를 받을때 같은 타입으로 주 생성자에서 초기화 해주는 부분이 있어야 된다면 부 생성자에서 타입변경 로직이 있어도 크게 문제되지 않을 거라고 생각한다.

3.1(스터디이후) 아니 없는게 좋을거 같아

  • 생성자는 객체 생성이라는 책임만 있을뿐 타입 변경 로직의 책임까지 있지 않다.
  • 변경 로직의 책임은 Converter와 같은 다른 객체에게 주고 변경된 값을 받아서 생성자는 생성하는 책임만 가지자
  • 그것이 생성자가 가지는 책임을 일관성있게 유지할 수 있는 방법이다.
    참고한 팀원의 우수한 블로그

4. 더 좋은 방법은 없을까?

토론하기를 보면 더 좋은 방법에 대한 고민이 무수히 많다.

  • 캐시를 사용하기 보다 Optional 의 if-condition 를 같이 사용한다면 더 light 해 보일 수 있다.

정리

1.1 : 객체는 책임과 캡슐화 능동성을 지닌 자율적인 존재 팩토리라 표현한다. 따라서 기능 종속적인 er 보다는 클래스 그 자체에 초점을 모은 네이밍을 사용하라

1.2 : 생성자 하나를 주 생성자로 만들어 코드 중복을 방지하고 설계를 단순화 하자

1.3 : 생성자에 코드를 넣지 말자. 실제 사용 시점까지 객체 변화 작업을 연기하자. 하지만 입력 변수의 타입변경은 부생성자에서 진행해도 괜찮을거같다(주 ctor 은 객체 초기화 프로세스를 시작하는 유일한 장소이므로)

참고 서적

엘레강트 오브젝트 - Yegor Bugayeno 저 | 조영호 역 | 지앤선

profile
클린코드 지향

0개의 댓글