티스토리 뷰

카테고리 없음

Constructors Must Be Code-Free

아빠악어. 2022. 2. 13. 15:29

아래 링크의 정리 및 요약입니다.

 

https://www.yegor256.com/2015/05/07/ctors-must-be-code-free.html

 

Constructors Must Be Code-Free

It is a bad idea to put executing statements into class constructors because that leads to side effects and uncontrollable behavior.

www.yegor256.com

생성자는 반드시 코드가 없어야 한다.

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;
  }
}

위 코드는 문제는 무엇일까? 한번만 name이 가공되고 encapsulates 되어 있으니 좋지 않은가? 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()에서 처리하는 것이 좋은 디자인이다.

 

첫번째 예제에서는 모든 연산을 즉시, 바로 처리한다. 이것은 절차적 프로그래밍이다.

하지만 선언적 프로그래밍에서는 모든 연산을 될 수 있는한 delay해야 한다.

 

아래와 같은 예는 어떤가?

final Name name = new EnglishName(
  new NameInPostgreSQL(/*...*/)
);
if (/* something goes wrong */) {
  throw new IllegalStateException(
    String.format(
      "Hi, %s, we can't proceed with your application",
      name.first()
    )
  );
}

첫번째 라인에서 우리는 단지 객체를 만들고 싶을 뿐인데 데이터베이스가 연결되고 fetch된다. 이것은 사이드 이펙트로 볼수 있다. 이 경우 어플리케이션은 더 느려질 것이다.

 

생성자에서 어떠한 연산을 가져가는 것은 bad practives이고 이것은 사이드이펙트 떄문에 피해야한다.

 

성능관점에서는 어떤가? name.first()가 다섯번 호출되면 String.split()가 다섯번 호출 될 것이다.

이를 해결하기 위해 우리는 새로운 클래스를 만들어야 한다.

 

public final class CachedName implements Name {
  private final Name origin;
  public CachedName(final Name name) {
    this.origin = name;
  }
  @Override
  @Cacheable(forever = true)
  public String first() {
    return this.origin.first();
  }
}

Cacheable annotation을 사용했다. Guava Cache나 다른 캐시를 사용 할 수 있을 것이다. 하지만 CachedName을 mutable하게 만들지는 말라. 이것은 Objects Should Be Immutable에서 설명했다.

final Name name = new CachedName(
  new EnglishName(
    new NameInPostgreSQL(/*...*/)
  )
);

이것은 primitive한 예지만 아이디어를 얻길 바란다.

 

이 디자인에서 우리는 오브젝트를 두개로 나누었다. 생성자에서 유일하게 허락된 구문의 할당임을 강조하고 만약 무엇인가 생성자안에 존재해야 한다면 리펙토링하고 디자인에 대해 다시 생각하라

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함