All Articles

클린 코드 스터디 (10): 클래스

10. 클래스

클래스에 대해 살펴봅시다.

클래스 체계

자바의 클래스 정의 표준 관례는 아래와 같은 순으로 코드 작성을 기대합니다:

  1. 변수 목록(public static 우선, private static이 그 다음, 마지막으로 private 순. 공개변수가 필요한 경우는 거의 없습니다)
  2. 공개 함수
  3. 비공개 함수

이래서 신문기사처럼 읽힌다고 말합니다.

캡슐화

변수나 유틸리티 함수는 반드시 숨기는 편이 좋겠습니다만, protected로 두어 테스트 코드가 접근하기 쉽도록 하기도 합니다. 최대한 private 상태로 두는 것을 강조합니다.

클래스는 작아야 한다

클래스는 작아야 합니다. 그럼 얼마나 작아야 할까요? 클래스는 “책임”을 적게 져야합니다.

이에 대한 책의 첫째 예시는 public 메소드가 70개가 넘는 로직을 보유하고 있습니다. 무려 책 두쪽을 할애했습니다. 어지럽네요.. 둘째 예시는 메소드를 5개만 가지고 있는 클래스가 나옵니다. 하지만 “책임”을 많이 지고 있는 것으로 보입니다.

클래스 이름은 해당 클래스의 책임을 기술해야 합니다. 간결한 이름은 간결한 클래스임을 입증합니다. 이름이 복잡하다면 책임이 클 가능성이 높습니다.

단일 책임 원칙(Single Responsibility Principle, SRP)

클래스, 모듈을 변경할 이유는 단 하나 뿐이어야 한다 라는 원칙입니다. 책임이 하나여야 한다는 뜻입니다. 객체지향 설계를 이야기 할 때 나오는 SOLIDS입니다.

소프트웨어는 돌아가는 소프트웨어가 최고입니다. 그를 위해서라면 돌아가는 단일 책임을 “잘 정리해야” 합니다. 이런 단일 책임을 가진 작은 클래스들이 협력하여 시스템에 필요한 동작을 수행하도록 해야합니다.

응집도 (Cohesion)

클래스는 인스턴스 변수 수가 적어야 합니다. 각 클래스 메소드는 클래스 인스턴스 변수를 하나 이상 사용해야 합니다. 일반적으로 메소드가 변수를 더 많이 사용할 수록 메소드와 클래스는 응집도가 더 높습니다. 모든 인스턴스 변수를 메소드마다 사용하는 클래스는 응집도가 가장 높습니다.

‘함수를 작게’, ‘매개변수 목록을 짧게’ 짜다보면 몇몇 메소드가 쓰는 인스턴스 변수가 아주 많아질 때가 옵니다. 이 때 변수와 메소드를 분리하여 새 클래스로 쪼개면 응집도를 유지할 수 있습니다.

저자가 큰 함수를 작은 함수/클래스 여럿으로 바꾼 방법론을 살펴봅시다.

  1. 테스트 코드 작성
    1. 이 모든 것을 위해 동작을 검증하는 Test suite을 작성했습니다.
    2. 한 번에 하나씩, 수 차례에 걸쳐 코드를 변경하였습니다.
    3. 본래 코드와 동일하게 도는지 확인했습니다.
  2. 코드의 변화에 주목
    1. 좀 더 길고 서술적인 이름을 변수이름을 두었습니다.
    2. 코드에 주석을 달 목적으로 함수 선언와 클래스 선언을 활용했습니다.
    3. 가독성을 위해 공백과 형식을 맞추었습니다.
  3. 책임을 나누었습니다.
    1. 실행환경을 책임지는 클래스, 렌더링을 해주는 클래스, 로직이 들어간 클래스로 분리했습니다. 각 내용 수정은 각각의 클래스에서 수행해주면 됩니다.

변경하기 쉬운 클래스

대부분의 시스템은 지속적인 변경이 가해집니다. 바꿀 때마다 시스템은 의도치않게 돌 수 있겠지요. 깨끗한 시스템은 클래스를 체계적으로 정리하여 이 위험을 낮춥니다.

클래스에 ‘손을 대’는 계기는 시스템의 변경일 때가 가장 자연스럽습니다. 그 시기가 온다면 설계 개선에 대한 고민과 시도가 필요합니다.

예시코드를 보면 하나의 덩어리였던 Sql 클래스가 아래 장점을 가지게 되었습니다:

  1. 단독책임을 가지는 각각의 메소드와 공통 유틸리티로 분리되었습니다.
  2. 확장에 개방적이고 수정에 폐쇄적인 구조를 갖게 되었습니다.
    1. 확장: 파생 클래스를 통해 확장하기 쉽습니다
    2. 폐쇄: 새 기능은 다른 클래스에 닫아놓아서 수정에 폐쇄적입니다.

변경으로부터 격리

객체 지향 프로그래밍을 배울 때, Concrete class와 Abstract class 클래스를 배웁니다. 이는 추상구현과 상세구현의 분리를 시사합니다. 상세한 구현에 의존한 코드는 변화에 취약하기 때문이지요.

‘어떤 기능이 있음’을 먼저 생각하고, 그 구현체를 앞서 생각한 기능에 맞추어 개발할 필요가 있습니다. 이를 구현의 결합도를 낮춘다고 표현합니다. 결합도가 낮아지면 유연성과 재사용성 또한 높아집니다. 시스템이 잘 격리되어있으면 이해하기도 쉬워집니다.

이러한 클래스 설계원칙을 DIP(Dependency Inversion Principle)라고 부르며 이 또한 앞서말한 SOLID의 D 입니다.

Published Feb 10, 2023

Non scholæ sed vitæ discimus.

his/him