-
Notifications
You must be signed in to change notification settings - Fork 0
bhlee chapter 07 Behavior
출처: 켄트벡의 구현패턴 7장 행위
컴퓨터는 명령어를 순서대로 하나씩 수행한다.
이러한 프로그램의 행위(Behavior)를 표현하는 방법에 대해 알아본다.
-
조건문을 사용해 특정 상태에서만 코드를 수행할 수 있다.
-
루프를 사용해 반복적으로 코드를 수행할 수 있다.
-
메시지를 사용해 서브루틴을 수행할 수 있다.
-
예외를 사용해 현재 흐름을 벗어나(pop) 이전 스택을 수행할 수 있다.
-
프로그램의 과정이 어디서 시작하고 어디서 끝나는지에 대한 주요 흐름을 명확히 표현해야한다.
-
흔치 않은 상황이나 에러 상황은 예외와 조건절을 사용해서 표현하면 된다.
-
객체지향 프로그래밍의 좋은 점 중 하나는 같은 프로시저를 사용해도 더 풍부한 내용을 전달할 수 있다는것이다.
-
아래 코드는 **"연산 과정은 3단계이며, 지금 당장 자세한 내용을 알 필요는 없다"**는 뜻을 갖고 있다.
compute() { input(); process(); output(); }
-
메시지를 제어 흐름의 메커니즘으로 사용하면 프로그램에서는 상태의 변화가 중요해진다.
-
아래와 다형적 메시지를 사용하면 명시적 조건문의 사용을 크게 줄일 수 있으며, 추후 확장이 쉽다.
public void displayShape(Shape subject, Brush brush) { brush.display(subject); }
-
위와 같이 선택 메시지를 사용하게 되면, 코드를 읽을 때 연산의 세부 구현을 이해하기 위해 여러 개의 클래스를 살펴봐야 한다.
-
선택 메시지가 일차원적 변형에 적합한데,
<script src="https://gist.github.com/libliboom/4abf138ef543a44e90a8cc5a22525ecc.js"></script>
두 가지의 독립적인 차원에서의 변형을 표현하기 위해서는 2개의 선택 메시지를 직렬로 연결해야 한다.
-
여러 단계로 구성된 복잡한 알고리즘이 있다면, 관련된 단계들을 모으고 이를 수행하기 위해 메시지를 보낼 수 있다.
-
메시지의 목적은 구현이 아닌, 함수를 나눈 뒤 그 나누어진 일부 항목을 호출하기 위한 도구일뿐이다.
-
분리된 메세지의 이름은 코드를 보고 이름만으로 이후 단계에서 어떤 일이 일어나는지 알 수 있도록 지어야 한다.
-
아래와 같이 대칭성을 이용하면 코드의 가독성을 높일 수 있다.
void compute() { input(); process(helper); // vs helper.process(this); output(); } void process(Helper helper) { helper.process(this); }
-
대칭성과 같은 "미학적"인 요소는 코드 패턴 만큼이나 코드 품질에 향상을 도모할 수 있는 중요한 요소이다.
-
하위클래스의 어떤 연산을 변형시킬 수 있음을 전달하기 위해 적당한 이름의 메시지로 알려주는것이 좋다. (jdk or sdk 예제 찾아 보기)
-
이름으로 메시지를 변형 가능성의 메시지를 전달하기 어렵다면 추상 메소드 선언을 고려해야한다.
-
소프트웨어 개발에서 개발자의 의도와 구현을 구분하는 것은 언제나 중요하다.
-
아래와 같이 한줄로 된 코드에 주석을 붙이고 싶은 경우, 커뮤니케이션을 위해 설명 메시지 사용을 고려해야 한다.
// example flags |= LOADED_BIT; // 로드 비트를 설정 // recommend setLoadedFlag();
-
예외 흐름은 주요 흐름의 명료성을 훼손하지 않는 범위 내에서 조건절이나 예외를 사용해 표현한다.
-
다양한 수행 경로를 동등하게 표현하면 프로그램에 플래그가 난무하고 특별한 의미를 가진 값들을 반환해야 한다.
-
아래와 같이 보호절을 사용하면 간단한 지역적 예외 상황을 지역적인 변화만을 수반하며 표현할 수 있다.
void initialize() { if(!isInitialized()) { ... // then 에 해당 하는 코드를 보면서 } // else 조건을 찾게 된다. } void initialize() { if(isInitialized()) return ; // 초기화가 된 경우 // 초기화가 되지 않은 경우 // (초기화 메소드가 여러번 호출되도 실제 초기화는 단 한번만 수행) // (초기화가 수행되는 코드가 제어 흐름에서 중요한 부분을 차지하는 경우) }
-
if-then-else는 동등한 중요성을 갖고 있는 제어 흐름을 표현한다.
-
보호절은 한 쪽의 제어 흐름이 다른 쪽보다 중요한 경우 유용하다.
-
여러개의 조건이 있는 경우 보호절을 사용하면 복잡한 제어 구조를 사용하지 않고도 구현할 수 있다.
<script src="https://gist.github.com/libliboom/d7d7f24b759e608f130bc96488c29934.js"></script> <script src="https://gist.github.com/libliboom/b11e7c1ac6079ae1299a4d1ab3e6b2c5.js"></script>
-
스택 상에서 한참 위쪽에서 문제가 발생했다면(디스크가 가득차거나 네트웍 연결이 끊어진 경우) 이런 문제는 스택의 한참 아래쪽에서 해결하는것이 합리적이다.
-
예외를 발견한 쪽에서는 예외를 던지고, 예외를 처리하는 쪽에서 예외를 받는 편이 그 사이의 모든 코드에서 예외를 처리하지도 못하면서 예외를 체크하고 전달하며 코드를 지저분하게 하는 것보다는 훨씬 낫다. (코드 예제 찾아보기)
-
조건절과 메시지로 구현할 수 있는 코드를 예외로 구현하면 단순 제어 흐름 구조 이외에 어떤 일이 벌어지는지 알아내야 하기 때문에 코드를 읽고 이해하기가 훨씬 어렵다.
-
가능하면 순차적 구문, 메시지, 루프, 제어문 중에서 선호하는 방식으로 제어 흐름을 표현하고 이 방법이 주요 흐름의 이해를 방해하는 경우에만 예외를 사용하는것이 좋다.
-
Unchecked 예외를 던졌을 때 아무도 그 예외를 받지 않는 경우 프로그램 수행은 종료된다. 따라서 사용자에게 어떤 일이 일어났는지 알려주기 위한 정보를 출력하는 편이 좋다.
-
Checked 예외는 컴파일러에 의해 체크되기 때문에 그 예외를 받거나 다른 클래스로 념겨줘야 한다.
- 하위 수준의 예외는 문제를 진단하는데 유용한 정보를 제공해주는 경우가 많기떄문에 하위 수준 예외를 문제 해결에 도움되는 메시지를 출력할 수 있는 상위 수준 예외로 포장해 사용하면 좋다. (코드 예제 찾아보기)