이 세상은 순차적입니다. 일어날 사건의 순서를 미리 알 수 있기 때문에 사건 하나하나를 실행하는 코드를 짠 후 사건들을내 마음대로 엮을 수 있는 절차적 프로그램을 작성할 수 있습니다.
이 세상은 객체지향적이기도 합니다. 객체의 세계에서 객체들의 행동은 자연스럽게 뒤섞이기 때문에 예상치 못한 행동들의 조합이 나타날 수 있습니다.
절차적 프로그램은 예상치 못한 행동들의 조합을 프로그래밍하기 어렵습니다. 프로그래머가 모든 사건의 조합을 작성하기에는 힘들기 때문입니다. 이를 해결하기 위해서 객체지향 디자인이 등장했습니다. 객체지향 디자인은 이미 정해진 절차들의 묶음으로 생각하지 않고, 객체가 서로 주고 받는 메세지들의 연쇄로 파악해야합니다. 이로인해 프로그래머는 객체들의 연관성만 작성하고나면 예전 처럼 모든 사건의 조합을 작성할 필요가 사라집니다.
객체지향 디자인 공부에 앞서서
객체지향 디자인의 실패는 코딩 능력 부족이 아닌 관점의 실패입니다. 객체지향 디자인을 배우기위한 첫걸음은 객체지향적 관점을 얻는 것입니다. 객체지향적 관점을 얻고나면 나머지는 자연스럽게 해결됩니다.
디자인이 해결해 줄 수 있는 문제들
오늘 새로운 애플리케이션을 만든다고 상상해봅시다. 또, 이 애플리케이션을 작성하기 위한 모든 요구사항이 완벽하고 기획이 절대 바뀌지 않는다고 가정해봅시자. 이런 경우에 디자인은 중요하지 않습니다. 한 번 프로그램을 작성한 뒤 영원히 애플리케이션이 돌아가는 모습을 바라보면 됩니다. 하지만 프로그램은 반드시 변합니다. 유행, 시대, 기술의 발전, 유저의 변덕 등 모든 외부 조건으로부터 프로그램 기획은 변하기 마련입니다. 쉽게 변경할 수 있는 애플리케이션은 프로그래밍을 하는 과정도 즐겁고 확장하는 과정도 즐겁습니다. 하지만 그 반대의 경우 즐겁게 작업을 할 수 없습니다. 어쩌면 고통스러울 수 있습니다. 요구사항의 변경은 반드시 일어나기 때문에 디자인이 중요한 것입니다.
프로그램 수정이 어려운 이유
객체지향 애플리케이션은 상호작용하는 여러 부분으로 구성되어 있습니다. 여기서 여러 부분이 객체이고, 상호작용이 객체가 주고받는 메시지입니다. 여기서 송신하고 수신하는 객체가 두 객체 사이의 의존성을 만들어 내고, 이런 의존성이 애플리케이션을 수정하기 어렵게 만듭니다.
객체지향 디자인은 의존성을 관리하는 것이고, 의존성을 관리하는 기술이 객체지향 디자인입니다. 디자인이 결여되어 있을 때, 관리되지 않은 의존성은 재앙을 불러옵니다. 예를 들어 객체가 서로에 대해서 너무 많이 알고 있을 때 하나의 객체를 수정할 경우 그 객체와 연결된 다른 객체까지 수정해야 합니다. 이어서 다른 객체의 다른 객체까지 수정해야합니다. 별거 없는 기능을 추가할 때에도 모든 코드를 수정해야할 수 있습니다.
디자인 원칙
디자인 원칙을 뜻하는 SOLID는 객체지향 디자인의 잘 알려진 디자인 원칙 다섯 가지를 말합니다.
- 단일 책임(Single Responsibility)
- 개방-폐쇄(Open-Closed)
- 리스코프 치환(Liskov Substitution)
- 인터페이스 분리(Interface Segregation)
- 의존성 역전(Dependenc Inversion)
그 외에는 DRY와 데메테르의 원칙(Law of Demeter LoD)등이 있습니다.
디자인 패턴
객체지향 디자인은 원칙뿐만 아니라 패턴을 가지고 있습니다. 1995년에 출판된 한국 출판명 GoF의 디자인 패턴은 패턴이란 객체지향 소프트웨어 디자인에서 명확한 문제를 처리하는 간단하고도 우아한 해결책이라고 말합니다. 디자인 패턴은 동일한 문제를 같은 방식으로 해결할 수 있는 방법에 이름을 부여한 것을 말합니다. 디자인 패턴은 프로그래머들이 서로 소통하고 협업할 수 있는 도구를 제공해 주었습다.
패턴이 유명해지면서 초보 프로그래머가 패턴을 오용하는 문제도 발생했습니다. 좋은 패턴을 잘못된 문제에 적용하면 복잡하고 혼란스러운 코드를 낳게 됩니다. 그렇기 때문에 올바른 패턴을 선택하고 제대로 사용할 수 있는 지식을 알아야 합니다.
조심해야할 점
디자인 없는 애플리케이션은 점점 더 수정할 수 없는 애플리케이션이 됩니다. 그러나 지나친 디자인을 적용한 애플리케이션도 마찬가지로 점점 더 수정할 수 없는 애플리케이션이 될 수 있습니다. 이런 경우는 원칙을 적용할 수 없는 곳에 원칙을 적용하고 패턴을 작성할 필요 없는 곳에서 패턴을 작성할때 나타납니다.
마지막으로 디자인은 반복적인 피드백과 함께하는 점진적인 발견의 과정입니다. 그리고 디자인은 지속적인 자기조절 능력을 갖추고, 자연스럽게 발전하는 것이 가장 좋습니다. 때문에 반복과 점진적인 발전을 중시하는 애자일 소프트웨어 개발은 잘 디자인된 애플리케이션을 만드는 데 매우 적합합니다.
언제 디자인을 해야하는가?
앞서 말한 애자일 소프트웨어 개발이 잘 디자인된 애플리케이션을 만드는 데 적합하다면 아래 두 가지 주장도 역시 사실입니다.
첫째, 커다란 디자인을 먼저 구상하는 방식(BUFD)을 취할 이유가 하나도 없다.
둘째, 애플리케이션이 완성되는 시점을 누구도 예상할 수 없다.
커다란 디자인을 먼저 구상하는 방식은 프로그래머와 고객이 서로 대립하게 만듭니다. 앞서 구성해놓은 커다란 디자인이 고객이 원하는 바일 확률은 상당히 낮기 때문에(흔히 고객은 자신이 원하는 바를 잘 알지 못합니다) 프로그램을 수정하게 되고 지켜야하는 일정이 있는 프로그래머는 수정사항들을 처리할 수 없다고 말합니다. 결국 프로젝트 자체도 점점 파멸의 길에 들어서게됩니다. 마감일이 다가왔는데도 프로젝트가 끝나지 않는다면 수정사항이 너무 많아서 그랬더라도 책임은 프로그래머가 집니다. 그렇기 때문에 애자일 개발을 통한 디자인 변경은 중간 중간 변하는 기획에 대해서 대처하기 위한 좋은 방법입니다.
결국 좋은 애플리케이션을 위해서는 초기에 큰 디자인을 구상하지 않고 계속해서 디자인을 변경해주는 게 좋습니다.
디자인 평가하기
프로그래머를 평가하는 데 있어서 코드 라인 수는 중요하지 않습니다. 오히려 코드 라인 수를 가지고 프로그래머를 평가하는 방식은 애플리케이션의 품질을 저하시키는 지름길입니다. 최근에는 코드 품질을 측정하는 새로운 측정법이 많이 등장했습니다. 이러한 측정 소프트웨어들은 소스 코드를 검사하여 몰랐던 문제나 개선할 수 있는 방법을 제공해줍니다. 잘 디자인된 애플리케이션처럼 보였을지라도 실제로는 객체지향 디자인의 원칙을 너무 많이 위반하고 있을 수 있습니다.
하지만 객체지향 디자인 점수가 높더라도 좋은 디자인을 구현하고 있다고 확신할 수는 없습니다. 다음번 수정이 쉽다고 보장해주지 않는다는 것입니다. 과도한 디자인은 ‘객체지향 디자인 점수’를 높게 받을 수 있겠지만 미래의 수정사항을 잘못 예상했다면 수정사항은 배로 높아질 것입니다.
결국 소프트웨어의 질을 측정하는 궁극적인 기준은 ‘주어진 시간 안에서의 기능별 구현 비용’일 것입니다. 만일 필요한 기능이 구현되어 있지 않아 당장 시장에서 퇴출될 상황이라면 유지보수 비용이 얼마나 증가할지는 중요하지 않습니다. 주어진 시간 이내에 최대한 빨리 구현하는 것이 중요합니다. 이러한 방식으로 타협하는 것은 미래의 시간을 빌려오는 것과 같습니다. 이 빚은 언젠가 갚아야 합니다. 그렇기 때문에 우리는 디자인의 손익분기점을 원하는 대로 조절하는 기술이 필요합니다.
참고
- 위키백과
- 루비로 배우는 객체지향 디자인
- GoF의 디자인 패턴