포스트

객체지향 설계 원칙: 다형성 활용과 명확한 의도 표현

객체지향 설계 원칙: 다형성 활용과 명확한 의도 표현

“한 줄 요약: instanceof 지양을 통한 다형성 확보와 메서드 명명(hasNext vs isEmpty)을 통한 비즈니스 의도 명확화”

1. 🚫 instanceof 사용을 지양해야 하는 이유

코드 내에서 객체의 실제 타입(예: Category, Product)을 직접 확인하는 instanceof는 가급적 피해야 한다고한다.

  • OCP (개방-폐쇄 원칙) 준수: 새로운 타입(예: 할인 상품, 기획전 카테고리)이 추가될 때마다 instanceof를 사용한 모든 조건문을 찾아 수정해야 합니다. 이는 버그 발생 가능성을 높이고 확장을 어렵게 만듭니다.
  • 낮은 결합도 (Low Coupling): 상위 시스템(CommerceSystem)이 하위 객체의 구체적인 클래스 정보를 너무 많이 알 필요가 없어야 합니다. 인터페이스에만 의존하게 하여 시스템 간의 결합도를 낮춥니다.
  • Tell, Don’t Ask 원칙: 객체에게 “Category?”라고 묻지 말고, 인터페이스에 정의된 메서드를 통해 “이 일을 수행해줘” 라고 시키는것이 객체지향적인 방식입니다.

2. 🔍 hasNext()isEmpty()의 의도적 분리

두 메서드의 반환값(boolean)이 논리적으로 유사하더라도, 각각의 역할과 의도를 명확히 분리하는 것이 중요합니다.

구분hasNext()isEmpty()
관점네비게이션 (구조)데이터 (상태)
의도“하위 단계로 더 탐색할 수 있는가?”“사용자에게 보여줄 내용물이 비어있는가?”
예시재귀 호출의 탈출 조건 결정“상품이 없습니다” 안내 메시지 출력 여부

상세 사유

  1. 가독성 (Readability): !node.hasNext()라는 부정형 조건문보다 node.isEmpty()라는 긍정형 조건문이 코드를 읽을 때 훨씬 직관적입니다.
  2. 의도의 명확성: hasNext()는 프로그램의 흐름 제어를 위해 존재하고, isEmpty()는 해당 도메인의 비즈니스 상태를 나타냅니다. 역할이 섞이지 않아야 코드가 깨끗해집니다.
  3. 유연한 확장성: 나중에 “하위 단계는 없지만(hasNext=false), 상단 공지사항은 보여줘야 하는 경우(isEmpty=false)”처럼 요구사항이 복잡해질 때, 두 메서드가 분리되어 있어야 기존 재귀 로직을 망가뜨리지 않고 대응할 수 있습니다.

3. 🛠️ 리팩토링 결과 요약

  • 중복 제거: 3중 중첩 while 루프를 단일 재귀 함수(Maps)로 통합하여 코드 라인 수를 줄이고 가독성을 높였습니다.
  • 유연성 확보: MenuNode 인터페이스를 통해 시스템이 어떤 계층(Depth)을 탐색하든 동일한 방식으로 동작하게 설계했습니다.

[리팩토링 적용 예시]

1
2
3
4
5
6
7
8
9
10
11
12
13
public void displayMenu(MenuNode node) {
    // 1. 상태 확인 (isEmpty) - 사용자에게 알림
    if (node.isEmpty()) {
        System.out.println("조회 가능한 항목이 없습니다.");
        return;
    }

    // 2. 구조 탐색 (hasNext) - 재귀적 출력
    node.printDetails();
    if (node.hasNext()) {
        displayMenu(node.getNextChild());
    }
}

4. 📝 오늘의 결론

객체를 선언하고 생성할 때 아무 생각 없이 코드를 작성하기보다, 어느 클래스에 책임이 있고 무슨 역할을 하는지 고민하는 습관을 가져야 한다. instanceof를 지우고 인터페이스의 메서드 명을 고민하는 과정 자체가 객체지향에 한걸음 더 다가가는 과정임을 느꼈다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.