티스토리 뷰
클래스 설계 초급편
초보자를 대상으로 클래스 설계와 관련된 내용을 알아보자. 클래스 설계는 경험이 필요하다. 일단 객체 지향 설계 원칙을 의식하는 것부터 시작해보자.
대략적인 클래스 조사
대상의 문제가 어떤 클래스로 이루어 졌는지 떠오르는 대로 적어 보자. 그리고 클래스 이름만으로 간단한 클래스 다이어그램을 작성해서 클래스의 관계를 구성한다. 여러 사람과 함께 토론하는 것이 혼자서 생각하는 것보다 훨씬 효율적이다. 처음부터 완전한 설계를 할 수 없다. 대략 적인 설계가 끝나면 코드를 작성하면서 진행하자. 코드를 작성하면서 재설계를 하면 된다. 초기 단계의 설계에 너무 집착하지 말자. 초기 설계를 버릴 각오를 하는 편이 좋다. 어느 정도 코드 작성이 진행될 떄까지는 문제의 본질이 잘 보이지 않을 수 있다. 문제의 본질을 깊게 이해하지 못했다면 클래스를 제대로 설계할 수 없다. 따라서 초기 단계에서는 두려워하지 말고 설계를 계속 변경하자. 여러 번 변경하다 보면, 점차 최종 모습이 보이고 문제의 본질을 알게 된다.
사용자 관점에서 멤버 함수의 사양을 결정
초기 단계의 러프한 설계에서부터 public 멤버 함수의 사양을 생각하자. 클래스는 역활과 책임이 중요하다. 역활과 책임이 결정되면 다른 멤버 함수의 사양도 조금씩 보이기 시작한다. 클래스 사용자의 관점에서 생각하는 것이 중요하다. 또한 읽기 좋은 직감적인 이름을 붙이자. 절대 작성자의 구현을 중심으로 생각해서는 안 된다.
멤버 함수 구현
멤버 함수의 사양이 결정되었다면 구현을 시작하자. 함수화 요령에서 설명했듯이 일단은 작성하자. 처음에는 큰 함수가 만들어져도 좋다. 중요한 것은 제대로 작동하는 코드를 작성하는 것이다. 멤버 함수가 제대로 작동하는 것을 확인 했다면 함수화 요령대로 작은 함수들로 분할하자. 멤버 함수를 분할하다 보면 클래스의 책임 관계가 명확해진다. 만일 멤버 함수의 책임이 클래스의 책임과 다르다면 클래스 분할을 고려해보자.
역할과 책임을 생각하면서 클래스 분할
초기 단계의 러프한 설계로부터 만들어지는 클래스는 덩치가 커지는 경향이 있다. 이때 클래스의 책임이 작아질 수 있도록 클래스를 분할한다. 멤버 변수 하나하나가 클래스로 만들 후보이다. 멤버 함수를 분할하다 보면 클래스 후보가 보일 것이다. 단일 책임 원칙을 바탕으로 외부로 뺄 수 있는 책임이 있다면 적극적으로 빼내어 다른 클래스로 이양하기 바란다.
클래스가 실제 작업하는 클래스인지, 다른 클래스를 관리하는 클래스인지 파악하면 책임을 나누기 쉬워진다. 실제 작업을 담당하는 클래스는 하나의 작업에 집중하도록 하고, 다른 클래스를 관리하는 클래스는 관리 작업에만 집중하도록 하자.
결합을 생각
부적절한 클래스와의 결합은 없는지 확인해 보자. 결합의 좋고 나쁨은 해당 클래스의 단위 테스트가 얼마나 간단할지 생각해보면 쉽게 판단할 수 있다. 단위 테스트가 어려운 클래스는 독립성이 낮고, 부적절한 결합 상태일 가능성이 크다. 부적절한 결합이 발견되었다면 추상 인터페이스를 작성해서 간접적인 결합으로 변경하자. 중간 계층을 추가함으로써 직접적인 결합 관계를 끊어버릴 수 있다.
캡슐화, 응집도, 결합도 확인
클래스가 완성되었다면 캡슐화가 제대로 되었는지, 응집도가 높은지, 결합도가 낮은지 확인하자. 클래스 응집도가 특히 중요하다 응집도가 높은 클래스는 역할과 책임이 명확해진다. 하나의 클래스가 하나의 책임을 가지는지 확인하자.
클래스 설계 중급편
클래스 설계에 익숙해졌다면, 한 단계 더 나아가서 프로그래밍 전체의 구조를 파악해보자. 지금부터 중간 규모 이상의 애플리케이션을 가정하고, 보수성과 이식성을 고려한 설계 방법을 소개한다.
문제 영역과 구현 부분 분리
문제 영역이란 해결해야할 문제의 핵심 부분이다. 문제 영역은 특정 환경에 의존하지 않게 설계해야 한다. 구현 영역이란 OS, 그래픽, 사운드 등의 API를 사용해서 문제 영역의 구현 방법을 제공하는 부분이다. 구현 영역은 작동 환경에 의존하다. 예를 들어 윈도우PC와 리눅스PC에서 사용하는 API가 다르므로 구현 영역이 다를 수 밖에 없다.문제 영역과 구현 부분을 분리하면, 구현 영역만 교환해서 다른 환경으로 이식할 수 있다.
문제 영역을 중심으로 설계
중간 규모 이상의 프로그램을 설계할 때는 문제 영역 중심으로 설계하자. 일부 초보자는 구현 수단에 비중을 두다 보니 구현 중심으로 설계하는 경우가 있다. 문제 영역은 애플리케이션 본체가 무엇인지를 명시해야 한다. 무엇, 어떻게에서 무엇부분이 문제 영역이고, 어떻게부분이 구현 영역이다. 어떻게만으로 구성된 코드는 갑작스럽게 세부 설명부터 시작하는 나쁜 문장과 비슷하다. 무엇부분에 해당하는 개요 설명이 없으므로, 코드를 많이 읽어본 뒤에야 무엇을 하는지 이해할 수 있게 된다.
추상 인터페이스로 분리
문제 영역과 구현 영역을 분리하는 방법으로 추상 인터페이스를 사용한다. 문제 영역이 요구하는 기능을 추상 인터페이스화 하는 것이다. 이렇게 하면 문제 영역에서는 특정 라이브러리 또는 OS의 API를 호출하지 않아도 된다. 이는 의존 관계 역전 원칙에 따르게 된다.
복잡한 것은 여러 개의 계층으로 분리
대규모의 복잡한 애플리케이션은 여러 개의 계층을 만들어 단계적으로 구현하자. 전체 구조를 계층화하는 것은 프로그래밍 설계 패턴 중 하나이다. 계층을 단계적으로 나누어 구현하면, 각 계층 클래스의 응집도가 높아지며 하나 하나의 클래스를 축소하는 효과가 있다.
클래스 설계 고급편
고급 편에서는 클래스 설계 이전의 단계에 관해 다뤄보겠다. 설계 방침을 잘 지키지 못하면 불필요한 작업이 많아져 생산성이 낮아진다. 초기 단계의 설계 방침은 모든 설계에 영향을 준다.
컨셉과 컨텍스트
좋은 설계를 하려면 컨셉(기본 개념)과 컨텍스트(문맥)을 고려해야 한다. 설계 의도를 명확히 하려면 컨셉이 필요하다. 예를 들어 '보수성을 우선한다' 또는 '실행 속도를 우선한다'등도 하나의 설계 컨셉이다. 클래스 또는 함수 단위의 설계에도 컨셉이 필요하다. 문제 해결에는 여러 방법이 있다. 컨셉이 정해져 있지 않으면, 설계할 때 고민이 생길 것이다. 하지만 컨셉이 명확하다면 최대한 컨셉에 가까운 방법을 선택하면 된다. 특히 여러 사람이 함께 개발한다면 컨셉을 잡는 것은 매우 중요하다. 컨셉이 결정되었다면 컨셉에 맞는 컨테스트를 생각하자.
규모에 맞는 설계
애플리케이션의 규모에 맞게 설계하자. 작은 것은 간단하게 만들 수 있다. 그런데 기능이 확장되어 규모가 커지면 해당 시점에 다시 설계해야 한다. 재설계를 하지 않고 대규모화되면 코드는 복잡해질 수 밖에 없다.
코드의 수명
코드에도 수명이 있다. 수명이 짧은 코드에 너무 많은 시간을 투자하는 것도 문제이다. 일회용 코드라면 오랜 시간을 두고 설계할 필요가 없다. 재사용할 가치가 있는 부분은 시간을 들여 설계하자. 코드를 재사용할 수 있게 설계하는 것은 상당한 시간과 노력이 필요하다. 항상 비용 대비 효율성을 고민해보자.
작업의 편리성
코드의 단순함보다는 작업의 편리성을 우선하자. 컴퓨터 입장에서 작업하는 대신 사람의 입장에서 작업하자. 사람이 편하기 위해서 컴퓨터가 존재한다. 쉽게 공동 작업을 할 수 있는 설계를 하고, 변경 또는 수정할 때 실수가 발생하지 않도록 설계하자.
하나의 방법에 관한 집착
문제를 해결하기 위한 방법은 다양하다. 친숙하다는 이유만으로 하나의 수단에 집착하지 말자. 객체 지향을 사용하는 것이 항상 좋은 선택은 아니다. 애플리케이션 특징에 맞는 프로그래밍 언어와 개발환경을 선택하자. '목적을 달성하기 위해 가장 효율적인 방법이 무엇일까?'를 생각하는 것이 중요하다.
최소한의 코드만 작성
코드를 작성할 필요가 없다면 작성하지 않는 것이 좋다. 코드의 양에 비례해서 보수하는 시간과 노력이 커지기 때문이다. 어떻게 코드를 작성할지가 아니라, 어떻게 하면 코드를 작성하지 않을지가 중요하다는 것을 잊지 말자. 함수화 또는 클래스화를 통해서 중복을 제거하고 재사용할 수 있도록 설계 하고, 표준 라이브러리 활용하는 등, 이 책에서 설명한 테크닉 역시 코드 양을 줄이는 효과가 있다. 이미 있는 것을 다시 만들 필요가 없다. 개발자의 목적은 코드를 작성하는 것이 아니라 애플리케이션을 작성하는 것이다.
마치며
객체 지향 설계를 간단하게 정리하면 코드를 정리 정돈하는 기술이라고 말할 수 있다. 복잡한 응용 프로그램을 클래스라는 작은 부품으로 분해해서 정리하는 것이다. 작은 부품을 조합해서 무엇인가를 만든다는 것과 같다. 원칙과 패턴에 너무 얽매이다 보면 이상한 집착이 생겨서 단순한 코드도 복잡하게 만드는 경향이 있습니다. 원칙을 사용하는 경우와 사용하지 않는 경우, 클래스를 분할하는 경우와 분할하지 않는 경우 중에 더 단순한 쪽을 선택하라. 고수일수록 좋은 의미의 편법을 잘 사용한다.
실력이 늘지 않는 것은 대부분 정보 입력이 부족하기 때문이다. 기술 관련 내용을 접하지 않거나, 다른 사람의 코드를 읽지 않는 것처럼 말이다. 개발자는 언제나 출력을 만들어 낼 수 있다. 중요한 것은 입력이다. 많은 것을 배우고, 적절하게 다룰 수 있도록 하자.
'객체지향 설계 > C와 C++ 게임코드로 알아보는 코딩의 기술' 카테고리의 다른 글
클래스 설계 요령 (0) | 2018.09.01 |
---|---|
객체 지향 설계의 원칙 (0) | 2018.08.31 |
객체 지향 설계의 기본 (0) | 2018.08.30 |
클래스를 만드는 요령 (0) | 2018.08.30 |
함수를 만드는 요령 (0) | 2018.08.30 |