깨끗한 클래스

깨끗한 클래스의 조건

클래스 정의를 깨끗하게 해야 한다

  • 필드 정의 순서
    • 변수목록이 먼저 나와야 한다.
      • public static
      • private static
      • private
    • public 필드는 지양한다.
  • 메서드 정의 순서
    • public 메서드
    • private 메서드는 자신을 호출하는 public 메서드 직후에 정의하여, 순차적으로 읽힐 수 있도록 한다
  • 캡슐화
    • 필드와 메서드는 최대한 비공개 상태로 유지하여 캡슐화를 지킨다

eslint 같은 도구를 활용해서 IDE가 자동으로 정리해줄 수 있게 하자.

클래스는 작아야 한다

  • 하나의 클래스는 하나의 책임을 가져야 한다
  • 책임이 여러개인지 확인하는 방법
    • 클래스 이름이 명확해야 한다 : 클래스 이름이 모호해진다면, 책임이 많아서 그렇게 된 경우다.
    • 클래스 설명을 짧게 할 수 있어야 한다 : 클래스 설명을 25단어 내외로, 접속사 없이 가능해야 한다.
    • 단일 책임 원칙 : 클래스는 변경할 이유가 하나뿐이여야 한다.
  • 응집도
    • 클래스는 자신과 관련된 일만 해야 한다

왜 클래스를 작게 만들어야 할까?

  • 변경을 쉽게 하기 위해서
    • 변경할 클래스를 쉽게 찾을 수 있고, 변경했을 때 문제가 생길 위험을 낮춰준다

변경하기 쉬운 클래스 만들기

시스템은 지속적으로 변경이 가해진다. 깨끗한 클래스는 변경에서 생길 수 있는 위험을 낮춰준다.

예를 들어, Lamp 클래스를 켜고 끌 수 있는 Switch 클래스가 있다고 생각해보자. 대략적인 코드는 아래와 같다.

class Switch {
  lamp: Lamp

  constructor(lamp: Lamp) {
    this.lamp = lamp
  }

  on() {
    this.lamp.activate()
  }

  off() {
    this.lamp.deactivate()
  }
}

class Lamp {
  activate() {
    // ...
  }

  deactivate() {
    // ...
  }
}
ts

이런 상황에서 Switch 클래스는 다음과 같은 문제점이 있다.

  • 구체 클래스인 Lamp에 의존한다. 즉, 다른 클래스와 결합도가 높다
  • Lamp를 다른 클래스로 교체하기 어렵다
  • 테스트하기 어렵다

구체 클래스가 아니라 추상화에 의존하는 방법으로 개선할 수 있다.
먼저, 켜고 끄는 API를 제공하는 인터페이스 Switchable을 추가한다.

interface Switchable {
  activate(): void
  deactivate(): void
}
ts

그리고 LampSwitchable을 구현하도록 하고,

class Lamp implements Switchable {
  // ...
}
ts

SwitchSwticable을 주입받도록 변경한다.

class Switch {
  switchable: Switchable

  constructor(switchable: Switchable) {
    this.switchable = switchable
  }

  public on() {
    this.switchable.activate()
  }

  public off() {
    this.switchable.deactivate()
  }
}
ts

그 결과, Switch클래스는 변경하기 쉬워졌다.

  • 구체 클래스가 아니라 추상화(인터페이스)에 의존하게 되었다
    • Lamp 클래스와 결합도가 낮아졌다
    • Lamp 클래스의 변경에 영향을 받지 않게 되었다
  • 의존성을 외부에서 주입받게 되었으므로, 의존성을 교체하는 것이 쉬워졌다
    • 테스트용 Switable을 만들어서 테스트하는 것도 가능하다