본문 바로가기
책(독서)

[켄트벡의 구현패턴] ~121p 행위

by DevJR 2020. 9. 15.

1. 제어흐름

- 코드가 실행되는 순서(흐름)를 의미

try {
  // 1 번째 실행흐름

} catch (Exception e) {
  // 1번째 실행흐름의 예외시
}

실행흐름은 보통 순서대로 수행이 되나 위처럼 try문 내의 흐름만 이루어지거나

예외시 흐름으로만 이루어지는 경우도 있다.

 

2. 주요 흐름

- 프로그램 연산에는 주요 흐름이 있다. 위에서 든 예에서 보면 try 문 내에 주요 흐름이 들어가야 하고, 이를 명확히 해야 한다. catch문(예외처리문)이 중요하지 않은것은 아니나 try문이 더 주요한 흐름이기에 그게 더 명확해야 한다는 의미이다.

 

3. 메시지

- 메시지화 해서 로직을 구현한다. (큰 틀에서 메시지 형태로 구현해두고 세부적인 내용은 메소드 내에서 처리하게끔 하라는 뜻인 것 같다.) ~한 것을 처리할 것이다(또는 해라) 라는 틀을 잡아두고 그 실질적인 세부 구현은 알아서 되게끔.

(사실 결과만 잘 나오면 세부 구현이야 어떤식으로 구현하든 의미는 크지 않을듯)

 

4. 선택 메시지

- 다형적 메시지를 통해 런타임 시에 메시지를 선택할 수 있게 한다. 이는 확장성을 야기한다. 

다만, 확장성이 당장에 필요하지 않은 경우라면 굳이 선택 메시지를 사용할 필요는 없다.

public void drawSymbol(Shape shape, Tool tool) {
	tool.draw(shape);
}

> tool은 런타임시 어떤 도구를 쓸 것인지 선택할 수 있다. (tool의 형태를 지닌 객체는 어떤 것이나 올 수 있다->다형성)

 

5. 더블 디스패치

- 위의 예에서 일차원적인 변형은 잘 표현이 되었으나, 두 가지의 독립적인 차원에서의 변형을 표현하려면 더블 디스패치라는 방법을 이용할 수 있다. (책으로는 잘 이해가 가질 않아서 구글링 해서 이해가 잘 되는 블로그 내용을 링크로 건다.)

wonwoo.ml/index.php/post/1490

 

토비의 봄 (더블 디스패치) - 머루의개발블로그

이번 시간에는 저번시간에 이어서 더블 디스패치에 대해서 알아보자. 더블 디스패치라는 용어는 자바가 나오기 훨씬 전에 어떤 논문으로 발표된 용어이다. 내 기억에는 내가 태어날 때 나왔으��

wonwoo.ml

6. 분리(순차) 메시지

- 복잡한 알고리즘이 존재할 경우 메시지를 나눌 수 있다. 메시지를 나눌 때에는 코드를 보고 이름만으로 이후 단계에 대한 내용을 알 수 있게 이름을 지어야 한다. 만일 이렇게 구분되게 (쉽게)이름지을 수 없다면 분리하는 게 맞는지 재검토 해봐야 한다.

 

7. 되돌림 메시지

void calculate() {
    inputValue()
    calculator.process(this);
    outputValue()
}

이런 로직은 대칭성이 떨어진다. 중간 처리 로직이 위 아래 로직과 너무 다른 형태이기 때문이다.

이럴 때, 책에서는 '도우미 메소드' 를 활용하여 다음과 같이 표현할 수 있다고 한다.

void process(Calculator cal) {
	cal.process(this);
}

void calculate() {
    inputValue();
    process(cal);
    ouputValue();
}

process라는 도우미 메소드를 통해 원래 로직은 파악하기 쉬운 대칭성을 띄게 되었다.

하지만, 과한 되돌림 메시지 사용은 아예 해당 도우미 메소드들을 위한 새로운 객체(클래스)를 생성하여 해당 객체에서 구현하게끔 하는게 나을 수도 있는 결과를 야기한다. 

더불어 이러한 되돌림 메소드가 성능이나 이런것으로는 그닥 큰 효과가 없을지라도 미학적으로 나은 로직을 구현하는 것은 중요하다고 말한다. (아마도 추후 유지보수할 때의 비용이 크기 때문이 아닐까 생각한다.)

 

8. 초청 메시지

- 하위 클래스의 어떤 연산에 대해 다른 사람들이 변형시킬 것이라 여겨지면 적당한 이름의 메시지를 통해 변형의 여지를 마련했음을 알려주는 것이 좋다고 한다. 이러한 메시지를 초청 메시지라 한다.

 

9. 설명 메시지

- 한줄로 된 코드에 주석을 붙이고 싶은 경우, 해당 로직을 잘 설명하는 메소드명을 정해 (역할없는 껍데기일뿐일지라도) 감싸주는 게 좋다라고 한다. 이를 통해 프로그래머의 의도를 명확히 할 수 있다. (이를 설명메시지라 한다.)

단적인 예일 수 있으나 다음과 같다.

int level = 0; //레벨을 0으로 초기화

initLevel();

주석을 써서 표현할 내용을 메소드명을 통해(메시지로) 간단히 의도를 전달할 수 있다. 

 

10. 예외흐름 

- 위에서 먼저 언급한 주요 흐름과 관련하여 예외처리할 부분은 별도로 처리할 수 있다. 하지만 결국 주요 흐름이 중요한 부분이기에 예외 흐름은 주요 흐름의 명료성을 훼손시키지 않는 범위 내에서 명료하게 처리할 것-!

 

11. 보호절 

- if~else문의 구문을 사용하지 말고(지양하고) if와 같은 조건문이 있을 경우 return을 활용한다. -> 이 형태가 보호절이다.

void init() {
   if (isOne()){
     // ~ 코드
   } else {
     // ~ else코드
   }
}

이런 형태보다는..

void init() {
   if (isOne()){
   	  return;
   }
  //~다른 코드	
}

이게 낫다는 것이다. (가독성 측면에서 좋다.)

for문이나 while문과 같은 반복문 내에서는 조건문 사용시 conitnue를 사용하는 것이 보호절을 사용하는 예시이다.

 

12. 예외 

- 예외는 문제 발생시 그 후의 나오는 로직을 다 처리하지 않고, 문제 발생 위치에서 예외를 던지면 받는 쪽에서 예외를 처리하기만 하면 되는 구조라서 간편하다. 다만, 예외는 일종의 설계상 누수 이므로 비용이 발생한다. 그래서 되도록 예외 말고 조건문이나 메시지, 순차적 구문 등으로 처리할 수 있으면 그렇게 하는 것이 좋다. 그렇게 처리하는 것이 주요 흐름에 대한 이해를 방해할 경우에만 예외를 사용하는 것이 좋다.

 

13. 체크 예외

- 예외의 위험성은 예외 던졌을 때, 아무도 그 예외를 받지 않을 경우이다. 이럴 경우 (사용자 입장에선) 아무 이유 없이 프로그램이 종료되었다고 생각할 수 있다. 예외 발생시에는 발생한 이유와 어떤 상태가 되었는지 알려주어야 한다. 이를 필수적으로 처리하게 해둔 것이 체크 예외이다. 이 체크 예외는 로직도 자리를 많이 차지하고 가독성도 떨어뜨리기 때문에 비용이 크다는 단점이 있긴 하다.

 

14. 예외 전달

- 예외 전달시에는 클라이언트 단에 전달할 예외와 프로그래머가 전달받을 예외를 구분할 필요가 있는 것 같다. (책의 의도는 이걸 말한 것 같다고 이해함) -> 하위 수준 에러는 프로그래머만 알면 되기 때문에 Log 등을 이용해 전달한다.