03 프로그래밍 이론
각 패턴에는 어느 정도 이론적 배경이 있다.
실제 프로그래밍에서는 이론 외에 결정 사항에 영향을 미치는 많은 동력force이 존재한다.
동력은 가치value와 원칙priciple의 두 가지로 볼 수 있다.
가치는
- 타인과의 커뮤니케이션을 중시
- 과도한 복잡성을 피하고 유연한 코드 작성
이런 가치는 프로그래밍 할 때 내리는 모든 의사 결정에 영향을 미친다.
원칙은
- 중요하지만 직접 적용하기 어려운 가치
- 적용법은 명확하지만 조금은 지엽적인 패턴
이 둘 사이의 연결고리를 하는 것이 원칙이다.
결정을 못하는 상황에서 원칙을 이해하고 있다면, 다른 패턴들과의 일관성을 깨지 않으면서도 훌륭한 해결책을 찾을 수 있다.
패턴은 지금 당장 무엇을 해야 할지 알려주고,
가치는 패턴을 사용해야 하는 동기를 알려주며,
원칙은 동기를 행동으로 어떻게 바꿔줄지를 알려준다.
우리는 모두 이전 세대 프로그래머들의 경험을 통해 배운다.
하지만 절대적인 최고의 개발 스타일이란 존재하지 않는다.
각자 가치와 원칙은 다양한 스타일을 만들기 때문이다.
가치
공통적인 가치는 커뮤니케이션, 단순성, 유연성이다.
세 가치는 상호 보완적인 경우가 많다.
그래서 최고의 프로그래머는 확장성을 고려하고, 불필요한 요소를 사용하지 않으며, 이해하기 쉬운 프로그램을 짠다.
커뮤니케이션
개발자가 코드를 쉽게 이해하고, 수정하고, 사용할 수 있다면, 그 코드는 개발자와 커뮤니케이션을 하고 있다고 이야기할 수 있다.
커뮤니케이션의 중요성은 크누스Knuth의 < Literate Programming글로 쓰는 프로그래밍 > 에서 프로그램을 책처럼 읽을 수 있어야 한다고 했다.
워드 커닝햄ward cunninham과 함게 그 책을 읽고 실천했고, 스몰토크 중에 ScrollController를 선택해서 코드에 이야기를 담는 연습을 했다.
코드로 설명하기 어려운 부분에 대해 어려운 이유를 쓰는 것 보다는 코드를 바꾸는 편이 더 쉬웠다.
이런 방식으로 프로그래밍을 하면 경제적 효과도 있다.
소프트웨어 비용의 대부분은 소프트웨어가 개발 된 이후에 발생한다.
기존 코드를 읽고 수정하는 데 걸리는 시간이 새로 작성하는데 걸리는 시간을 넘어선다.
커뮤니케이션에 관심을 가지도 보면 현실적인 관점에서 코드를 작성하게 된다.
다른 사람이 내 코드를 읽을 것이라는 생각에 더 많은 생각을 하면서 코드를 작성하므로 좋은 코드를 작성할 수 있다.
때로 나만의 관점에서 벗어나 새로운 시각에서 문제를 바라보고 신선한 해결책을 찾게 되기도 한다.
우리는 사회적 존재이므로 이런 문제를 무시하기 보다는 고려하는 쪽이 더 현실적이다.
단순성
에드워드 터프트Edward Tufte의 < The Visual Display of Quantitative Information수량적 정보의 시각적 표현 > 에는 그래프에서 쓸모 없는 점들을 지우는 문제를 소개한다.
이렇게 해서 그래프를 만들면 원본에 비해 이해하기 쉬운 그래프가 된다.
복잡도를 낮추면 프로그램을 읽고 사용하고 수정하는 사람들이 프로그램을 훨씬 빨리 이해할 수 있다.
프로그래밍을 할 때 해야 할 일 중 하나는 지금까지 짜 놓은 코드를 보고 불필요한 부분을 제거하는 것이다.
단순성Simplicity은 독자의 수준에 따라 달라진다.
전문 프로그래머에게는 쉽고 단순한 내용이어도 초보자에게는 복잡해 보일 수 있다.
좋은 글을 쓰려면 독차를 고려해야 하듯 좋은 프로그램도 역시 독자 수준을 고려해야 한다.
단순성을 추구하다 보면 혁신을 이룰 수 있다.
프로그래밍 언어도 C, C++, Java 순서대로 단순하게 발표했다가 조금식 복잡해지고 있고
JUnit 역시 처음에 단순한 기능만 있었다가 JUnit 4에 이르러 많이 복잡해졌다.
그러다 보면 또 JUnit보다 단순한 테스트 도구가 나올 것이며 이런 새로운 아이디어가 또 다른 혁신을 이뤄낼 것이다.
커뮤니케이션과 단순성은 때로 협업 관계에 있다.
복잡도가 과하지 않다면 시스템을 이해하기 쉽다.
커뮤니케이션에 신경 쓸수록, 어느 부분이 과도하게 복잡한지 알기 쉽다.
과도한 단순화로 프로그램을 이해하는데 어려워진다면 커뮤니케이션 쪽을 선택하는게 좋다.
유연성
유연성은 비효율적인 코딩이나 설계를 정당화해주는 가치이다.
프로그램은 유연해야 하지만 발전적인 방향으로 유연해야 한다.
유연성을 위해 과도한 복잡성을 가져온다면 이득이 없다.
당장 필요할 것 같은 유연성을 위해 초기에 작업해 뒀다가 나중에 필요 없다는 게 밝혀지는 경우가 있다.
그래서 추측에 의해 설계에 유연성을 부여하는 것 보다는 단순성을 유연하게 적용한 후 테스트를 철저하게 하는 편이 더 효과적이다.
유연성이 있으면서도 당장 이득을 볼 수 있는 패턴을 쓰는게 좋다.
향후 이득이 있을지 모르는 불확실한 패턴의 경우는 사용을 보류했다가 필요한 때가 오면 그때 적용하라.
유연성은 복잡도를 증가시킨다.
단순성은 유연성을 촉진시킨다.
커뮤니케이션을 중시하면 유연성도 좋아진다.
프로그래머는 더욱 단순하고 이해하기 쉬우면서 수정이 쉬운 코드를 작성하면
최종적으로 유연성을 높일 수 있게 된다.
원칙
원칙을 이해하고 있다면, 각 패턴의 세부 내용 뿐 아니라 패턴을 사용하는 근본적인 이유에 대해 알 수 있으므로 새로운 상황에 부딪혔을 때 적절히 대처할 수 있다.
지역적 변화
코드를 수정할 때 함게 바꿔야 하는 부분을 최소화하라.
지역적 변화로만 수정한 코드는 커뮤니케이션이 쉽다.
구현 패턴을 사용하는 중요한 이유는 프로그램 수정 비용을 낮추기 위함이므로, 지역적 변화Local Consequences의 원칙은 여러 패턴의 근간이 된다.
최소 중복
지역적 변화를 돕는 다른 원칙은 최소 중복Minimize Repetition의 원칙이다.
중복이 꼭 반복되는 코드 외에도 병렬 클래스 계층parallel class hierarchy도 중복의 일종이다.
한 번 수정에 2개 이상의 클래스 계층에서 수정이 일어나면 지역적 변화의 원칙을 위배하는 것이다.
중복은 예측하기 어렵고 있다고 해도 제거하기 어려울 수도 있다.
따라서 피해야 할 요소라기 보다는 수정 비용을 높이는 위험 요소 정도로 보는 게 좋다.
중복을 없애는 한 가지 방법으로 프로그램을 여러 작은 부분으로 나눈다.
작은 부분은 짧은 구문, 짧은 메소드, 작은 객체, 작은 패키지 등이 있다.
프로그램에서 동일한 부분, 유사한 부분, 다른 부분을 명확히 표현한다면
프로그램을 읽고 이해하기 쉽고 수정 비용도 낮아진다.
로직과 데이터의 결합
프로그램 수정이 필요하면 로직과 데이터 모두 고쳐야 하는 경우가 많다.
그래서 같이 있으면 수정해도 그 영향력을 지역적 변화 수준으로 만들 수 있다.
대칭성Symmetry
add() 메소드가 있으면 remove() 메소드가 있다.
같은 파라미터를 사용하는 여러 메소드가 있을 수도 있다.
대칭성을 찾아서 명확히 표현해주면 코드를 읽기 수월해진다.
대칭성이 있다는 걸 알면 프로그램 절반만 이해해도 나머지 절반은 자연스럽게 이해가 되기 때문이다.
일반적인 대칭성의 의미는 좌우bilateral, 회전rotational 등이 있지만
프로그램의 대칭성은 하나의 아이디어를 프로그램 전체에서 일관된 방식으로 표현하는 통일성이라고 할 수 있다.
선언적 표현
명령형 프로그래밍imperative programming은 강력하고 유연하지만 프로그램을 이해하려면 흐름을 따라가야 한다.
수행 순서가 중요하거나 조건부 구문이 없는 경우는 선언적 표현을 통해 코드를 작성하는 것이 이해가 쉽다.
변화율
함께 변하는 로직과 데이터는 함께 관리하고,
변화율이 다른 로직과 데이터는 분리하는 것이다.
변화율은 시간적 대칭성으로 볼 수 있다.
때로 변화율 원칙은 프로그래머가 수정하는 내용에 해당하다.
변화율은 데이터에도 적용된다.
하나의 객체에 있는 모든 필드는 가급적 함께 변해야 한다.
대칭적인 필드를 별도의 객체에 묶어서 대칭성을 표현하면, 코드를 읽는 다른 사람에게 프로그래머의 의도를 잘 전달할 수 있고 중복을 줄일 수 있으며 코드 수정에 대한 영향을 제어하기 쉬워진다.
03 프로그래밍 이론
각 패턴에는 어느 정도 이론적 배경이 있다.
실제 프로그래밍에서는 이론 외에 결정 사항에 영향을 미치는 많은 동력force이 존재한다.
동력은 가치value와 원칙priciple의 두 가지로 볼 수 있다.
가치는
이런 가치는 프로그래밍 할 때 내리는 모든 의사 결정에 영향을 미친다.
원칙은
이 둘 사이의 연결고리를 하는 것이 원칙이다.
결정을 못하는 상황에서 원칙을 이해하고 있다면, 다른 패턴들과의 일관성을 깨지 않으면서도 훌륭한 해결책을 찾을 수 있다.
패턴은 지금 당장 무엇을 해야 할지 알려주고,
가치는 패턴을 사용해야 하는 동기를 알려주며,
원칙은 동기를 행동으로 어떻게 바꿔줄지를 알려준다.
우리는 모두 이전 세대 프로그래머들의 경험을 통해 배운다.
하지만 절대적인 최고의 개발 스타일이란 존재하지 않는다.
각자 가치와 원칙은 다양한 스타일을 만들기 때문이다.
가치
공통적인 가치는 커뮤니케이션, 단순성, 유연성이다.
세 가치는 상호 보완적인 경우가 많다.
그래서 최고의 프로그래머는 확장성을 고려하고, 불필요한 요소를 사용하지 않으며, 이해하기 쉬운 프로그램을 짠다.
커뮤니케이션
개발자가 코드를 쉽게 이해하고, 수정하고, 사용할 수 있다면, 그 코드는 개발자와 커뮤니케이션을 하고 있다고 이야기할 수 있다.
커뮤니케이션의 중요성은 크누스Knuth의 < Literate Programming글로 쓰는 프로그래밍 > 에서 프로그램을 책처럼 읽을 수 있어야 한다고 했다.
워드 커닝햄ward cunninham과 함게 그 책을 읽고 실천했고, 스몰토크 중에 ScrollController를 선택해서 코드에 이야기를 담는 연습을 했다.
코드로 설명하기 어려운 부분에 대해 어려운 이유를 쓰는 것 보다는 코드를 바꾸는 편이 더 쉬웠다.
이런 방식으로 프로그래밍을 하면 경제적 효과도 있다.
소프트웨어 비용의 대부분은 소프트웨어가 개발 된 이후에 발생한다.
기존 코드를 읽고 수정하는 데 걸리는 시간이 새로 작성하는데 걸리는 시간을 넘어선다.
커뮤니케이션에 관심을 가지도 보면 현실적인 관점에서 코드를 작성하게 된다.
다른 사람이 내 코드를 읽을 것이라는 생각에 더 많은 생각을 하면서 코드를 작성하므로 좋은 코드를 작성할 수 있다.
때로 나만의 관점에서 벗어나 새로운 시각에서 문제를 바라보고 신선한 해결책을 찾게 되기도 한다.
우리는 사회적 존재이므로 이런 문제를 무시하기 보다는 고려하는 쪽이 더 현실적이다.
단순성
에드워드 터프트Edward Tufte의 < The Visual Display of Quantitative Information수량적 정보의 시각적 표현 > 에는 그래프에서 쓸모 없는 점들을 지우는 문제를 소개한다.
이렇게 해서 그래프를 만들면 원본에 비해 이해하기 쉬운 그래프가 된다.
복잡도를 낮추면 프로그램을 읽고 사용하고 수정하는 사람들이 프로그램을 훨씬 빨리 이해할 수 있다.
프로그래밍을 할 때 해야 할 일 중 하나는 지금까지 짜 놓은 코드를 보고 불필요한 부분을 제거하는 것이다.
단순성Simplicity은 독자의 수준에 따라 달라진다.
전문 프로그래머에게는 쉽고 단순한 내용이어도 초보자에게는 복잡해 보일 수 있다.
좋은 글을 쓰려면 독차를 고려해야 하듯 좋은 프로그램도 역시 독자 수준을 고려해야 한다.
단순성을 추구하다 보면 혁신을 이룰 수 있다.
프로그래밍 언어도 C, C++, Java 순서대로 단순하게 발표했다가 조금식 복잡해지고 있고
JUnit 역시 처음에 단순한 기능만 있었다가 JUnit 4에 이르러 많이 복잡해졌다.
그러다 보면 또 JUnit보다 단순한 테스트 도구가 나올 것이며 이런 새로운 아이디어가 또 다른 혁신을 이뤄낼 것이다.
커뮤니케이션과 단순성은 때로 협업 관계에 있다.
복잡도가 과하지 않다면 시스템을 이해하기 쉽다.
커뮤니케이션에 신경 쓸수록, 어느 부분이 과도하게 복잡한지 알기 쉽다.
과도한 단순화로 프로그램을 이해하는데 어려워진다면 커뮤니케이션 쪽을 선택하는게 좋다.
유연성
유연성은 비효율적인 코딩이나 설계를 정당화해주는 가치이다.
프로그램은 유연해야 하지만 발전적인 방향으로 유연해야 한다.
유연성을 위해 과도한 복잡성을 가져온다면 이득이 없다.
당장 필요할 것 같은 유연성을 위해 초기에 작업해 뒀다가 나중에 필요 없다는 게 밝혀지는 경우가 있다.
그래서 추측에 의해 설계에 유연성을 부여하는 것 보다는 단순성을 유연하게 적용한 후 테스트를 철저하게 하는 편이 더 효과적이다.
유연성이 있으면서도 당장 이득을 볼 수 있는 패턴을 쓰는게 좋다.
향후 이득이 있을지 모르는 불확실한 패턴의 경우는 사용을 보류했다가 필요한 때가 오면 그때 적용하라.
유연성은 복잡도를 증가시킨다.
단순성은 유연성을 촉진시킨다.
커뮤니케이션을 중시하면 유연성도 좋아진다.
프로그래머는 더욱 단순하고 이해하기 쉬우면서 수정이 쉬운 코드를 작성하면
최종적으로 유연성을 높일 수 있게 된다.
원칙
원칙을 이해하고 있다면, 각 패턴의 세부 내용 뿐 아니라 패턴을 사용하는 근본적인 이유에 대해 알 수 있으므로 새로운 상황에 부딪혔을 때 적절히 대처할 수 있다.
지역적 변화
코드를 수정할 때 함게 바꿔야 하는 부분을 최소화하라.
지역적 변화로만 수정한 코드는 커뮤니케이션이 쉽다.
구현 패턴을 사용하는 중요한 이유는 프로그램 수정 비용을 낮추기 위함이므로, 지역적 변화Local Consequences의 원칙은 여러 패턴의 근간이 된다.
최소 중복
지역적 변화를 돕는 다른 원칙은 최소 중복Minimize Repetition의 원칙이다.
중복이 꼭 반복되는 코드 외에도 병렬 클래스 계층parallel class hierarchy도 중복의 일종이다.
한 번 수정에 2개 이상의 클래스 계층에서 수정이 일어나면 지역적 변화의 원칙을 위배하는 것이다.
중복은 예측하기 어렵고 있다고 해도 제거하기 어려울 수도 있다.
따라서 피해야 할 요소라기 보다는 수정 비용을 높이는 위험 요소 정도로 보는 게 좋다.
중복을 없애는 한 가지 방법으로 프로그램을 여러 작은 부분으로 나눈다.
작은 부분은 짧은 구문, 짧은 메소드, 작은 객체, 작은 패키지 등이 있다.
프로그램에서 동일한 부분, 유사한 부분, 다른 부분을 명확히 표현한다면
프로그램을 읽고 이해하기 쉽고 수정 비용도 낮아진다.
로직과 데이터의 결합
프로그램 수정이 필요하면 로직과 데이터 모두 고쳐야 하는 경우가 많다.
그래서 같이 있으면 수정해도 그 영향력을 지역적 변화 수준으로 만들 수 있다.
대칭성Symmetry
add() 메소드가 있으면 remove() 메소드가 있다.
같은 파라미터를 사용하는 여러 메소드가 있을 수도 있다.
대칭성을 찾아서 명확히 표현해주면 코드를 읽기 수월해진다.
대칭성이 있다는 걸 알면 프로그램 절반만 이해해도 나머지 절반은 자연스럽게 이해가 되기 때문이다.
일반적인 대칭성의 의미는 좌우bilateral, 회전rotational 등이 있지만
프로그램의 대칭성은 하나의 아이디어를 프로그램 전체에서 일관된 방식으로 표현하는 통일성이라고 할 수 있다.
선언적 표현
명령형 프로그래밍imperative programming은 강력하고 유연하지만 프로그램을 이해하려면 흐름을 따라가야 한다.
수행 순서가 중요하거나 조건부 구문이 없는 경우는 선언적 표현을 통해 코드를 작성하는 것이 이해가 쉽다.
변화율
함께 변하는 로직과 데이터는 함께 관리하고,
변화율이 다른 로직과 데이터는 분리하는 것이다.
변화율은 시간적 대칭성으로 볼 수 있다.
때로 변화율 원칙은 프로그래머가 수정하는 내용에 해당하다.
변화율은 데이터에도 적용된다.
하나의 객체에 있는 모든 필드는 가급적 함께 변해야 한다.
대칭적인 필드를 별도의 객체에 묶어서 대칭성을 표현하면, 코드를 읽는 다른 사람에게 프로그래머의 의도를 잘 전달할 수 있고 중복을 줄일 수 있으며 코드 수정에 대한 영향을 제어하기 쉬워진다.