6. 조건분기 - 미궁처럼 복잡한 분기 처리를 무너뜨리는 방법

내용


조건 분기가 중첩되어 낮아지는 가독성

  • early return 으로 조건 분기 중첩을 제거하자

  • elseif 를 early return으로 개선할 수 있는지 확인해보자

switch 조건문 중복

int costMagicPoint(MagicType magicType, Member member) {
    int magicPoint = 0;

    switch (magicType) {
        case fire:
            magicPoint = 2;
            break;
        case lightning:
            magicPoint = 5 + (int)(member.level * 0.2);
            break;
    }

    return magicPoint;
}
  • 마법 사용 포인트.. 케이스가 추가될 때 마다 case.... 코드가 추가된다.

    • 별로 좋지 못한 코드

      • 새로운 케이스가 추가될 때마다 비즈니스 로직이 누락되는 경우가 발생할 수 있음.

개선방법 - 단일책임원칙

  • 단일책임 원칙에 따라서 Magic과 관련된 모든 것을 관리하는 클래스를 만든다.

class Magic {
    final String name;
    final int costMagicPoint;
    final int attackPower;
    final int costTechnicalPoint;

    Magic(final MagicType magicType, final Member member) {
        switch (magicType) {
            case fire:
                name = "파이어";
                costMagicPoint = 2;
                attackPower = 20 + (int)(member.level * 0.5);
                costTechnicalPoint = 0;
                break;
            case lightning:
                name = "라이트닝";
                costMagicPoint = 5 + (int)(member.level * 0.2);
                attackPower = 50 + (int)(member.agility * 1.5);
                costTechnicalPoint = 5;
                break;
            case hellFire:
                name = "헬파이어";
                costMagicPoint = 16;
                attackPower = 200 + (int)(member.magicAttack * 0.5 + member.vitality * 2);
                costTechnicalPoint = 20 + (int)(member.level * 0.4);
                break;
            default:
                throw new IllegalArgumentException();
        }
    }
}
  • 마법 이름, 매직포인트 소비량, 공격력, 테크니컬포인트 소비량..을 관리하는 하나의 클래스

  • 이렇게 한곳에서 관리할 경우, 누락 실수를 줄일 수 있음.

  • 인터페이스를 이용한다.

    • 클래스가 거대해지면 관심사에 따라 작은 클래스로 분할하는 것이 중요.

    • 인터페이스?

      • 기능 변경을 편리하게 할 수 있는 개념

개선방법 - 인터페이스 이용 (전략 패턴)

// 마법 자료형
interface Magic {
    String name();
    int costMagicPoint();
    int attackPower();
    int costTechnicalPoint();
}
// 마법 '파이어'
class Fire implements Magic {
    private final Member member;

    Fire(final Member member) {
        this.member = member;
    }

    public String name() {
        return "파이어";
    }

    public int costMagicPoint() {
        return 2;
    }

    public int attackPower() {
        return 20 + (int)(member.level * 0.5);
    }

    public int costTechnicalPoint() {
        return 0;
    }
}
  • 인터페이스 구현체

  • Map으로 이용하도록 변경하기

final Map<MagicType, Magic> magics = new HashMap<>();


void magicAttack(final MagicType magicType) {
    final Magic usingMagic = magics.get(magicType);

    showMagicName(usingMagic);
    consumeMagicPoint(usingMagic);
    consumeTechnicalPoint(usingMagic);
    magicDamage(usingMagic);
}


void showMagicName(final Magic magic) {
    final String name = magic.name();

}


void consumeMagicPoint(final Magic magic) {
    final int costMagicPoint = magic.costMagicPoint();

}


void consumeTechnicalPoint(final Magic magic) {
    final int costTechnicalPoint = magic.costTechnicalPoint();

}


void magicDamage(final Magic magic) {
    final int attackPower = magic.attackPower();

}

자료형 확인에 조건 분기 사용하지 않기

  • instanceof 자료형을 판정하는 연산자

  • 리스코프 치환 원칙 을 위반

    • 클래스의 기반 자료형과 하위 자료형 사이에 성립하는 규칙

    • 기반 자료형을 하위 자료형으로 변경해도 코드는 문제 없이 동작해야한다.

    • 기반 자료형?

      • 인터페이스

    • 하위 자료형?

      • 인터페이스를 구현한 클래스

플러그 매개변수

  • damage(true, damageAmount)

    • true -> 플러그 매개변수

    • 어떤 내부적으로 어떤일을 하는지 예측하기 힘듬

  • 메서드는 하나의 기능만 하도록 설계하는 것이 좋음

정리


  • 중첩 if 문 사용하지 말라 -> 가독성 떨군다.

  • switch 문 대신, 인터페이스를 활용한 전략패턴을 고려하라 -> 누락 실수를 방지할 수 있다.

  • 조건 분기를 사용하는 상황이 오면 일단 인터페이스 설계를 떠올리자

참고


  • https://product.kyobobook.co.kr/detail/S000202521361

Last updated

Was this helpful?