본문 바로가기

디자인패턴/내 코드가 그렇게 이상한가요?

분기문 적게 쓰는 법 - 내 코드가 그렇게 이상한가요? 6장 요약 (1)

 

조기 리턴

조건을 만족하지 않는 경우 바로 리턴하는 방법이다. 다음은 if문이 여러번 중첩되어 알아보기 힘든 코드 예시이다. 

// 살아 있는가
if (member.getHitPoint() > 0) {
    // 움직일 수 있는가
    if (member.canAct()) {
        // 매직포인트가 남아 있는가
        if (magic.getCostMagicPoint() <= member.getMagicPoint()) {
            member.consumeMagicPoint(magic.getCostMagicPoint());
            member.chant(magic);
        }
    }
}

 

다음은 조기 리턴으로 개선한 코드이다. 살아있는가?를 죽었는가?로 반전하여 조기리턴할 수 있게 되었다.

if (member.hitPoint <= 0) return; 
if (!member.canAct()) return; 
if (member.magicPoint <magic.costMagicPoint) return;

member.consumeMagicPoint(magic.costMagicPoint); 
member.chant(magic);

 

 

switch 조건문은 하나로 모으기

동일한 switch 조건분기를 여러번 작성하는 대신 switch는 한 번만 쓰도록 리팩토링 하는 것이 좋다. 다음은 수정전 코드이다. `magicType`에 의해 이름, 매직 포인트, 어택 파워 등 여러가지가 분기되어야 하는 상황이다. 이 경우에는 만약 `magicType`이 새로 추가되는 경우나 혹은 magicType에 따라 분기처리해야하는 로직이 추가되는 경우 수정해야 하는 코드가 많다.

class MagicManager {
	String getName (MagicType magicType) {
    	 String name = "";
        
         switch (magicType) {
            case fire: 
            	name "파이어"; 
                break; 
            case lightning: 
                name = "라이트닝";  
                break;
        }
		return name; 
    }
    
    int costMagicPoint (MagicType magicType, Member member) {
    	 int magicPoint = 0;
        
         switch (magicType) {
            case fire: 
            	magicPoint = 2; 
                break; 
            case lightning: 
                magicPoint = 5;
                break;
        }
		return magicPoint; 
    }
    
    int attackPower (MagicType magicType, Member member) {
    	 int attackPower = 0;
        
         switch (magicType) {
            case fire: 
            	attackPower = 20 + (int)(member.level * 0.5); 
                break; 
            case lightning: 
                attackPower = 20 + (int)(member.agility * 1.5); 
                break;
        }
		return attackPower; 
     }
}

 

수정한 코드는 다음과 같다. switch 조건문을 하나로 통일했다. 

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; 
            default: 
            	throw new IllegalArgumentException();
            }
        }
    }
}

 

 

switch를 인터페이스로 대체하기

switch를 하나로 통일시킨다해도 타입의 갯수가 늘어나면 switch문이 비대해질 우려가 있다. 따라서 switch를 인터페이스로 대체하는 것이 바람직하다. 우선 여러가지 `MagicType`을 `Magic`이라는 인터페이스로 묶는다. 모든 MagicType은  이름 조회(`name()`), 소모 매직 포인트 계산(`costMagicPoint()`), 공격 파워 계산(`attackPower()`) 세 가지 분기를 해야하므로 이를 메서드로 정의한다.

 

interface Magic {
    String name(); 
    MagicPoint costMagicPoint(); 
    AttackPower attackPower();
}

 

 

그리고 각 MagicType을 인터페이스의 구현체로 정의한다. 다음은 Fire 매직 타입에 대한 Concrete Class이다. 

class Fire implements Magic { 
    private final Member member;
    Fire(final Member member) {
        this.member =member;
    }
    public String name() {
        return "파이어";
    }
    public MagicPoint costMagicPoint() {
        return new MagicPoint(2);
    }
    public AttackPower attackPower() {
        final int value = 20 + (int)(member.level *0.5); 
        return new AttackPower(value);
    }
    public TechnicalPoint costTechnicalPoint() 
        return new TechnicalPoint(0);
    }
 }