本文へ移動
cccskills
無料GitHub で公開

replace-conditional-with-poly

타입·상태별로 반복되는 switch/if-else 분기를 다형성(인터페이스 + 구현 클래스)으로 치환. "switch를 다형성으로", "타입 분기를 클래스로", "strategy 패턴", "/replace-conditional-with-poly" 요청 시 사용. 단, null 분기만이면 /introduce-special-case, 조건식 가독성만이면 /decompose-conditional이 적합. /replace-conditional-with-poly [commit-ref]로 호출.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md7.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Replace Conditional with Polymorphism Skill

반복되는 switch/if-else 조건문을 다형성으로 치환하여 Open-Closed Principle 적용.

GOAL

  • 성공 = 반복 조건문이 다형성으로 치환되어 커밋 완료됨
  • 동일한 switch/if-else가 2곳 이상에서 반복됨
  • Type Code 기반 분기가 식별됨
  • 사용자 확인 후 다형성 패턴 적용
  • 모든 테스트 통과

CONSTRAINTS

  • 계열: System-wide — 후보 제시 후 승인 받고 적용 (../../references/refactoring-procedure.md §0·§3-B)

Hard Rules

  • 동작 변경 금지 — 구조 개선만 수행
  • 테스트 수정 금지 — 구조 변경이 테스트를 실패시키면 되돌리기
  • 사용자 확인 없이 리팩토링 금지 — 모든 후보는 사용자 승인 후 실행
  • 커밋 단위 — 1파일 x 1기법 = 1커밋 (논리적으로 연결된 파일은 함께)
  • git add -A 금지 — 변경된 파일만 명시적으로 추가

적용 패턴

Replace Conditional with Polymorphism 리팩토링 경로:

  1. Replace Type Code with Subclasses

    • Type code (String, enum 등)를 서브클래스로 변환
    • 각 타입별 서브클래스 생성
  2. Replace Constructor with Factory

    • 직접 생성자 호출을 Factory Method로 치환
    • Type code에 따라 적절한 서브클래스 반환
  3. Replace Conditional with Polymorphism

    • switch/if-else를 추상 메서드로 치환
    • 각 서브클래스에서 구체적 구현

Before/After 예시

// Before: 반복되는 조건문 (2곳 이상)
public class Performance {
    private Play play;
    private int audience;
    
    public int amount() {
        switch (play.type) {
            case "tragedy":
                int result = 40000;
                if (audience > 30) {
                    result += 1000 * (audience - 30);
                }
                return result;
            case "comedy":
                int result = 30000;
                if (audience > 20) {
                    result += 10000 + 500 * (audience - 20);
                }
                result += 300 * audience;
                return result;
            default:
                throw new IllegalArgumentException("unknown type: " + play.type);
        }
    }
    
    public int volumeCredits() {
        int result = Math.max(audience - 30, 0);
        // 동일한 조건문 반복
        if ("comedy".equals(play.type)) {
            result += Math.floor(audience / 5);
        }
        return result;
    }
}

// After: 다형성으로 치환
abstract class PerformanceCalculator {
    protected Play play;
    protected int audience;
    
    static PerformanceCalculator create(Play play, int audience) {
        switch (play.type) {
            case "tragedy": return new TragedyCalculator(play, audience);
            case "comedy": return new ComedyCalculator(play, audience);
            default: throw new IllegalArgumentException("unknown type: " + play.type);
        }
    }
    
    PerformanceCalculator(Play play, int audience) {
        this.play = play;
        this.audience = audience;
    }
    
    abstract int amount();
    
    int volumeCredits() {
        return Math.max(audience - 30, 0);
    }
}

class TragedyCalculator extends PerformanceCalculator {
    TragedyCalculator(Play play, int audience) {
        super(play, audience);
    }
    
    @Override
    int amount() {
        int result = 40000;
        if (audience > 30) {
            result += 1000 * (audience - 30);
        }
        return result;
    }
}

class ComedyCalculator extends PerformanceCalculator {
    ComedyCalculator(Play play, int audience) {
        super(play, audience);
    }
    
    @Override
    int amount() {
        int result = 30000;
        if (audience > 20) {
            result += 10000 + 500 * (audience - 20);
        }
        result += 300 * audience;
        return result;
    }
    
    @Override
    int volumeCredits() {
        return super.volumeCredits() + (int) Math.floor(audience / 5);
    }
}

// 사용
public class Performance {
    private PerformanceCalculator calculator;
    
    public Performance(Play play, int audience) {
        this.calculator = PerformanceCalculator.create(play, audience);
    }
    
    public int amount() {
        return calculator.amount();
    }
    
    public int volumeCredits() {
        return calculator.volumeCredits();
    }
}

적용 기준

Replace Conditional with Polymorphism을 적용해야 하는 경우:

  1. 조건문 반복: 동일한 switch/if-else가 2곳 이상에서 반복
  2. Type Code 기반: String, enum 등의 타입 코드로 분기
  3. 새 타입 추가 빈번: 새로운 케이스가 자주 추가됨
  4. 분산된 로직: 타입별 로직이 여러 메서드에 분산
  5. OCP 위반: 새 타입 추가 시 기존 코드 수정 필요

OUTPUT FORMAT

실행 절차

공통 골격(대상 파일 수집 → 후보 제시(계열별 승인 규칙) → 적용 → 테스트 → 커밋/되돌리기, 브랜치·PR이 필요한 조건)은 이 스킬 디렉터리 기준 ../../references/refactoring-procedure.md가 정본이다. 아래는 이 기법에 고유한 부분만 규정한다.

반복 조건문 후보 식별 (공통 절차 2단계)

대상 파일에서 다음 패턴을 찾는다:

  • 동일한 조건 변수로 분기하는 switch/if-else가 2곳 이상
  • Type code (String, enum)로 분기
  • 3개 이상의 case/branch
  • 각 branch가 5줄 이상의 로직 포함

후보 제시 예시 (공통 절차 3단계)

후보를 하나씩 제시하고 사용자 확인:

## 리팩토링 후보 1: Replace Conditional with Polymorphism

**파일**: Performance.java
**대상**: play.type 기반 조건문 (2곳에서 반복)

**반복 위치**:
1. amount() 메서드 (라인 15-30)
2. volumeCredits() 메서드 (라인 35-40)

**현재 코드**:
[조건문이 반복되는 코드 블록]

**제안 변경**:
1. PerformanceCalculator 추상 클래스 생성
2. TragedyCalculator, ComedyCalculator 서브클래스 생성
3. Factory Method로 적절한 서브클래스 생성
4. switch 문을 추상 메서드 호출로 치환
5. 각 서브클래스에서 타입별 로직 구현

**적용할까요?** (yes / no / 수정 요청)
  • 사용자가 yes → 실행 목록에 추가
  • 사용자가 no → 스킵
  • 사용자가 수정 요청 → 요청 반영 후 재제시

모든 후보 확인 후 최종 실행 목록을 보여주고 진행 여부 확인.

다형성 치환 실행 (공통 절차 4단계)

확정된 리팩토링을 하나씩 수행:

  1. 추상 클래스 생성 및 공통 로직 이동
  2. Type code별 서브클래스 생성
  3. Factory Method 추가
  4. 조건문을 추상 메서드 호출로 치환
  5. 각 서브클래스에서 구체적 구현

커밋 메시지 형식:

refactor: replace conditional with polymorphism in [클래스명]

결과 보고

사용자에게 보고:

  • 적용된 다형성 치환 목록

FAILURE CONDITIONS

공통 실패 조건(계열별 승인 규칙 위반, 테스트 실패 방치, 테스트 수정, 커밋 단위, git add -A, heredoc 한글 메시지)은 ../../references/refactoring-procedure.md에 있다. 아래는 이 기법에 고유한 것만.

  • 1곳에만 있는 단순 조건문에 적용 (불필요한 복잡도 증가)
  • Factory Method 없이 직접 서브클래스 생성자 노출
  • 모든 조건문을 다형성으로 치환 (간단한 조건문은 유지하는 것이 나음)

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

동일한 결과를 내는 여러 조건문(OR 나열·중첩 AND)을 하나로 통합하고 의미 있는 boolean 메서드로 추출. "조건문 합쳐", "같은 결과 반환하는 if 정리", "중첩 if 평탄화", "/consolidate-conditional" 요청 시 사용. 단, 여러 메서드에 흩어진 동일 조건을 호출자 쪽으로 올리는 것은 /lift-up-conditional, 복잡한 조건식·분기를 메서드로 쪼개는 것은 /decompose-conditional이 적합. /consolidate-conditional [commit-ref]로 호출.

日本語の概要は準備中です。原文の説明を表示しています。

msbaek/msbaek-claude-plugins82026年10月1日 更新

기능의 external behavior를 Cucumber 인수 테스트(주 검증층)로 구축 — .feature 실행으로 문서↔코드 드리프트를 구조적으로 차단, Four Layer(Steps→Protocol Driver→SUT), 태그 기반 가역 제외, 기존 JUnit 인수 테스트 이관. "인수 테스트 도입", "Gherkin을 실행 가능하게", "cucumber 셋업" 요청 시 사용. /cucumber-acceptance로 호출.

日本語の概要は準備中です。原文の説明を表示しています。

msbaek/msbaek-claude-plugins82026年10月1日 更新

복잡한 if/then/else의 조건식과 각 분기를 의미 있는 메서드로 추출하여 가독성 향상. "조건문 분해", "if 가독성", "복잡한 조건식에 이름 붙여", "/decompose-conditional" 요청 시 사용. 단, 같은 결과를 내는 조건문들을 하나로 합치는 것은 /consolidate-conditional, 타입별 분기를 클래스로 바꾸는 것은 /replace-conditional-with-poly가 적합. /decompose-conditional [commit-ref]로 호출.

日本語の概要は準備中です。原文の説明を表示しています。

msbaek/msbaek-claude-plugins82026年10月1日 更新

Primitive Obsession 제거 — 검증·연산이 따라다니는 primitive 필드(금액+통화, 이메일 문자열 등)를 도메인 개념을 담은 Value Object로 치환. "값 객체 도입", "primitive obsession", "Money 클래스로", "/discover-value-object" 요청 시 사용. 단, 함께 전달되는 파라미터 묶음을 객체로 바꾸는 것은 /introduce-parameter-object, 컬렉션을 감싸는 것은 /first-class-collection이 적합. /discover-value-object [commit-ref]로 호출.

日本語の概要は準備中です。原文の説明を表示しています。

msbaek/msbaek-claude-plugins82026年10月1日 更新

컬렉션 getter가 내부 List/Set을 직접 노출하는 것을 방지 — unmodifiable 반환 + add/remove 메서드 제공. "컬렉션 캡슐화", "getter가 List 그대로 노출", "unmodifiable로", "/encapsulate-collection" 요청 시 사용. 단, 컬렉션과 관련 로직을 전용 클래스로 뽑는 것은 /first-class-collection이 적합. /encapsulate-collection [commit-ref]로 호출.

日本語の概要は準備中です。原文の説明を表示しています。

msbaek/msbaek-claude-plugins82026年10月1日 更新

암묵적 의존성(전역 변수·클래스 필드·싱글턴 접근)을 명시적 파라미터로 전환하여 메서드 투명성 향상. "숨은 의존성 드러내", "필드 대신 파라미터로", "전역 참조 제거", "/explicit-parameters" 요청 시 사용. 단, 파라미터가 많아져 묶어야 하면 /introduce-parameter-object, I/O와 계산 분리는 /segregate-functional-core가 적합. /explicit-parameters [commit-ref]로 호출.

日本語の概要は準備中です。原文の説明を表示しています。

msbaek/msbaek-claude-plugins82026年10月1日 更新

msbaek のスキルをすべて見る

このスキルの問題を報告する