좋은코드 나쁜코드: 프로그래머의 코드 품질 개선법(PART I 이론)
CHAPTER 2 추상화 계층
- 문제와 하위 문제를 어떻게 해결하는가도 중요하지만 그것들을 해결하는 코드를 어떻게 구성하는가도 중요하다
- 코드를 잘 구성한다는 것은 간결한 추상화 계층을 만드는 것으로 귀결될 때가 많다
널값 및 의사코드 규약
많은 프로그래밍 언어에는 값(또는 참조/포인터)이 없다는 개념을 가지고 있다 이 개념을 표현하기 위해 넣 null 값을 사용한다.
- 값이 제공되지 않거나 함수가 원하는 결과를 반환할수 없는 경우가 자주 발생함 값이 없다 또는 부재한다는 이 개념은 유용하다
- 값이 널일수 있거나 혹은 널이면 안 되는 경우가 항상 명백한 것은 아니라서 문제가 발생한다
널 안전성, 보이드 안정성 대한 생각이 점점 추진력을 얻고 있다.
최근에 몇년간 새로 등장한 중요한 언어들 대부분은 널 안전성을 지원한다.
사용하는 언어가 널 안전성을 지원하지 않으면 널값을 사용하는 되신 옵셔널 타입을 사용하는것이 좋다
왜 추상화 계층을 만드는가?
하나의 문제가 있을 때 이 문제와 하위 문제에 대한 해결책이 일련의 층을 형성한다고 생각할수 있다. 이것을 추상화 계층이라고 한다
같은 층위 내에서는 쉽게 이해할 수있는 몇개의 개념만 다루기 때문에 개별 코드가 복잡해 보이지 않을것 이다
소프트웨어 엔지니어로서 문제를 해결할 때 이것이 목표가 되어야한다
비록 문제가 엄청나게 복잡할지라도 하위 문제들을 식별하고 올바른 추상화 계층을 만듦으로 복잡한 문제를 쉽게 해결 가능하다.
추상화 계층 및 코드 품질의 핵심 요소
- 가독성
- 깨끗하고 뚜렷한 추상화 계층을 만드는 것은 개발자가 한 번에 한두 개 정도의 계층과 몇 개의 개념만 다루면 된다는 것을 의미한다
- 모듈화
- 추상화 계층이 하위 문제에 대한 해결책을 깔끔하게 나누고 구현 세부 사항이 외부로 노출되지 않도록 보장할 때 다른 계층이나 코드의 일부에 영향을 미치지 않고 계층 내에서만 구현을 변경하기 매우 쉽다
- 재사용성 및 일반화성
- 하위 문제에 대한 해결책이 간결한 추상화 계층으로 제시되면 해당 하위 문제에 대한 해결책을 재사용하기 쉬워진다.
- 테스트 용이성
- 신뢰할수 있는 코드를 작성하고자 한다면 각 하위 문제에 대한 해결책이 견고하고 제대로 작동하는지 확인 해야 된다.
- 코드가 추상호 계층으로 깨끗하게 분활 되면 하위 문제에 대한 해결책을 테스트하는것이 쉬워진다.
코드의 계층
대부분의 프로그래밍 언어는 코드를 다른 단위로 나누기 위해 몇 가지 언어 요소를 자유롭게 사용할 수 있다
- 함수
- 클래스(및 구조체나 믹스인과 같이 클래스와 비슷한 요소도 가능)
- 인터페이스
- 패키지, 네임스페이스, 모듈
API 및 구현 세부사항
- 코드를 호출할 때 볼 수 있는 내용
- 퍼블릭 클래스, 인터페이스 및 함수
- 이름, 입력 매개변수 및 반환 유형이 표현하고자 하는 개념
- 코드 호출 시 코드를 올바르게 사용하기 위해 알아야 하는 정보(예: 호출순서)
- 우리가 작성한 코드도 미니 API를 노출하는 것으로 생각하면 유용할때가 있다.
함수
- 각 함수에 포함된 코드가 하나의 잘 써진 짧은 문장처럼 읽히면 이상적이다
- 함수를 작성하기 위한 좋은 전략
- 단일 업무 수행
- 잘 명명된 다른 함수를 호출해서 더 복잡한 동작 구성
- 함수를 작게 만들고 수행하는 작업을 명확하게 하면 코드의 가독성과 재사용성이 높아진다
클래스
- 단일 클래스의 이상적인 크기
- 줄수 : 한 클래스의 코드는 300줄이 넘지 않아야 된다.
- 300줄 보다 긴 클래스는 너무 많은 개념을 다룬다고 생각해서 분리해야 된다
- 그렇다고 해서 300줄 이하의 모든 클래스가 적절한 크기임을 보장하지 않음
- 응집력 : 한 클래스 내의 모든 요소들이 얼마나 잘 속해 있는지 보여주는 척도 좋은 클래스는 응집력이 강하다
- 순차적 응집력 : 한 요소의 출력이 다른 요소에 대한 입력으로 필요할 때 발생한다
- 기능적 응집력: 몇 가지 요소들이 모여서 하나의 일을 성취하는 데 기여할때 발생
- 관심사의 분리 : 시스템이 각각 별개의 문제를 다루는 개별 구성 요소로 분리되어야 한다고 주장하는 설계 원칙
- 줄수 : 한 클래스의 코드는 300줄이 넘지 않아야 된다.
- 코드 계층 및 클래스 생성과 관련한 네가지 핵심요소
- 코드 가독성
- 코드 모듈화
- 코드 재사용성 및 일반화
- 테스트 용이성 및 적절한 테스트
- 저품질 코드의 근거
- 코드를 읽을 수 없다
- 코드가 특별히 모듈화되어 있지 않다
- 코드를 재사용할 수 없다
- 코드를 일반화할 수 없다
- 코드를 제대로 테스트하기 어렵다
- 코드 개선 방법
- 하위 문제의 해결책을 자체 클래스로 분할하여 개선
- 의존성 주입을 활용한다
- 이점
- 코드가 좀 더 모듈화되고 재구성할 수 있게 됨
- 코드의 재사용성이 좀 더 높아짐
- 코드의 테스트 용이성이 좀 더 높아짐
인터페이스
- 계층 사이를 뚜렷이 구분하고 구현 세부 사항이 계층 사이에 유출 되지 않도록 하기 위해 사용할 수 있는 한가지 접근법은 잍터페이스를 통해 노출 여부를 결정하는것
- 추상화 계층을 깔끔하게 구현하는 코드를 만드는 데 있어 인터페이스는 매우 유용한 도구다
- 모든것을 위한 인터페이스?
- 장점
- 퍼블릭 API를 명확하게 보여준다
- 한 가지 구현만 필요하다고 잘못 추측한 것일 수 있다
- 테스트를 쉽게 할수 있다
- 같은 클래스로 두가지 하위 문제를 해결할 수 있다
- 단점
- 더 많은 작업이 필요하다
- 코드가 복잡해질 수 있다
- 장점
층이 너무 얇아질 때
- 코드를 별개의 계층으로 할때 단점
- 클래스를 정의하거나 의존성을 새 파일로 임포트하려고 반복적으로 사용하는코드로 인해 코드양이 늘어난다
- 로직의 이해를 위해 파일이나 클래스를 따라갈 때 더 많은 노력이 필요하다
- 인터페이스 뒤에 계층을 숨기게 되면 어떤 상황에서 어떤 구현을 사용되는지 파악하는데 노력이 필요
마이크로서비스는 어떤가?
마이크로 서비스도 여전히 그 내부에서 적절한 추상화 계층을 고려하는것이 유용하다
마이크로 서비스는 시스템을 분리하여 보다 모듈화할 수 있는 매우 좋은 방법이지만 서비스를 위해 여러하위 문제를 해결 하는것은 변하지 않는다
요약
- 코드를 깨끗하고 뚜렷한 추상화 계층으로 세분화하면 가독성, 모듈화, 재사용, 일반화 및 테스트 용이성이 향상된다
- 특정 언어에 국한된 기능뿐만 아니라 함수, 클래스 및 인터페이스를 사용하여 코드를 추상화 계층으로 나눌수 있다
- 코드를 추상화 계층으로 분류하는 방법을 결정하려면 해결 중인 문제에 대한 판단과 지식을 사용해야 한다