getter 메소드를 사용하지 않도록 리팩토링한다.

2017-11-01 11:09

오늘 코드 리뷰 주제는 지난 번에 이어 다음 그림과 같은 볼링 게임 점수판을 구현하는 프로그래밍의 일부이다.

bowling board

볼링 게임을 구현하면서 쓰러진 볼링 핀을 관리하는 클래스를 Pins 클래스로 다음과 같이 추상화했다.

public class Pins {
    private static final int MIN_PINS = 0;
    private static final int MAX_PINS = 10;
    
    private int falledPins;

    private Pins(int falledPins) {
        this.falledPins = falledPins;
    }
    
    private static void validPins(int falledPins) {
        if (falledPins > MAX_PINS) {
            throw new IllegalArgumentException(
                    String.format("볼링 핀은 최대 10을 넘을 수 없습니다. 현재 쓰러진 핀 수는 %d", falledPins));
        }
        
        if (falledPins < MIN_PINS) {
            throw new IllegalArgumentException(
                    String.format("볼링 핀은 최초 0 미만이 될 수 없습니다. 현재 쓰러진 핀 수는 %d", falledPins));
        }
    }

    public static Pins bowl(int falledPins) {
        validPins(falledPins);
        
        return new Pins(falledPins);
    }
    
    public int getFalledPins() {
        return falledPins;
    }

    [...]
}

쓰러진 볼링 핀의 수를 위와 같이 구현한 이유에 대해서는 반복되는 유효성 체크와 같은 중복 로직은 어떻게 제거할 수 있을까? 글을 참고한다.

그런데 위와 같이 구현한 결과 Spare, Miss 클래스 구현 코드가 다음과 같이 Pins에서 쓰러진 핀의 수(getFalledPins() 메소드 활용)를 가져오는 코드로 도배되는 문제가 발생한다.

class Miss extends Finished {
    private Pins firstPins;
    private Pins secondPins;

    Miss(Pins firstPins, Pins secondPins) {
        this.firstPins = firstPins;
        this.secondPins = secondPins;
    }

    @Override
    public Score getScore() {
        return new Score(this.firstPins.getFalledPins() + this.secondPins.getFalledPins(), 0);
    }

    public Score cacluateAdditionalScore(Score score) {
        score = score.bowl(this.firstPins.getFalledPins());
        if (score.canCalucateScore()) {
            return score;
        }
        score = score.bowl(this.secondPins.getFalledPins());
        return score;
    }

    @Override
    public String getDesc() {
        return this.firstPins.getFalledPins() + " | " + this.secondPins.getFalledPins();
    }
}

Pins를 통해 추상화함으로써 유효성 처리에 대한 중복 처리를 제거할 수 있었지만 매번 Pins에서 값을 가져와야 하는 불편함이 발생한다.

위와 같이 매번 Pins에서 쓰러진 핀의 수를 가져오는 부분을 리팩토링해보자.

힌트는 객체지향 생활체조 원칙의 "규칙 9: 게터/세터/프로퍼티를 쓰지 않는다."를 적용하는 방법을 검토해 보자.

위 샘플 예제는 코드스쿼드 에서 새롭게 진행 중인 마스터즈 코스에서 발췌한 코드입니다. 코드스쿼드의 마스터즈 코스는 코드 리뷰 방식의 개인별 맞춤 학습 방법입니다.

0개의 의견 from FB

1개의 의견 from SLiPP

2017-11-02 13:34

객체의 상태 데이터를 조회하는 getter 메소드 사용을 줄이는 방법은 객체에 메시지를 보내 상태 데이터를 가지는 객체가 로직을 처리하도록 구현하면 된다.

본문 글의 Miss 클래스에서 getFalledPins() 메소드를 사용하는 부분의 로직을 Pins로 이동할 수 있다.

public class Pins {
    private static final int MIN_PINS = 0;
    public static final int MAX_PINS = 10;
    
    private int falledPins;

    [...]
      
    public boolean isStrike() {
        return this.falledPins == MAX_PINS;
    }
    
    public boolean isSpare(Pins secondPins) {
        return totalPins(secondPins) == MAX_PINS;
    }

    public boolean isMiss(Pins secondPins) {
        return totalPins(secondPins) < MAX_PINS;
    }

    public int totalPins(Pins secondPins) {
        int totalPins = this.falledPins + secondPins.falledPins;
        validPins(totalPins);
        return totalPins;
    }

    public Score sumScore(Score score) {
        return score.bowl(this.falledPins);
    }

    public String getDesc() {
        if (isStrike()) {
            return "X";
        }
        
        return falledPins + " | ";
    }

    public String getDesc(Pins secondPins) {
        if (isSpare(secondPins)) {
            return falledPins + " | /";
        }
        
        return falledPins + " | " + secondPins.falledPins;
    }
}

위와 같이 falledPins을 사용하는 로직은 Pins에서 구현하도록 리팩토링했다. 이와 같이 리팩토링한 후의 Miss 클래스는 다음과 같이 구현할 수 있다.

package bowling.frame.state;

import bowling.frame.pin.Pins;
import bowling.frame.pin.Score;

class Miss extends Finished {
    private Pins firstPins;
    private Pins secondPins;

    Miss(Pins firstPins, Pins secondPins) {
        this.firstPins = firstPins;
        this.secondPins = secondPins;
    }

    @Override
    public Score getScore() {
        return new Score(firstPins.totalPins(secondPins), 0);
    }

    public Score cacluateAdditionalScore(Score score) {
        score = firstPins.sumScore(score);
        if (score.canCalucateScore()) {
            return score;
        }
        score = secondPins.sumScore(score);
        return score;
    }

    @Override
    public String getDesc() {
        return firstPins.getDesc(secondPins);
    }
}
의견 추가하기

연관태그

← 목록으로