Design Pattern

템플릿 메서드(Template Method) 패턴

태인킴 2020. 8. 18. 22:38
반응형


1. 템플릿 메서드(Template Method) 디자인 패턴

전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내용은 변경 가능 하도록 만든 디자인 패턴 입니다. 추상 클래스구현 클래스로 나누어, 로직의 기본 골격의 해당하는 부분은 템플릿 메서드(Template Method) 로 구현 하고, 로직의 확장/변경 가능한 부분을 추상 메서드(abstract method)로 정의하여 구현 클래스는 이 추상 메서드(abstract method)를 구현하여 사용 합니다. 

 

 

 

2. 몸무게와 키 국가별 로컬라이제이션(Localization)

abstract class LocaleItemFormatter{
    Pattern onlyNumberPattern = Pattern.compile("[^0-9.]");

    protected Locale locale;
    private String sysLanguage;
    private final String koreaLanguage = Locale.KOREA.getLanguage();
    private final String japanLanguage = Locale.JAPAN.getLanguage();
    private final String englishLanguage = Locale.ENGLISH.getLanguage();
    private final String chinaLanguage = Locale.CHINA.getLanguage();

    LocaleItemFormatter(Locale locale) {
        this.locale = locale;
        this.sysLanguage = locale.getLanguage();
    }

    public String convert(Double value) {
        if(sysLanguage.equals(koreaLanguage) || sysLanguage.equals(japanLanguage) || sysLanguage.equals(chinaLanguage)) {
            return convertAsEasternCountry(value);
        } else if(sysLanguage.equals(englishLanguage)) {
            return convertAsWesternCountry(value);
        } else {
            return convertAsEasternCountry(value);
        }
    }

    public Double inverse(String value) {
        if(sysLanguage.equals(koreaLanguage) || sysLanguage.equals(japanLanguage) || sysLanguage.equals(chinaLanguage)) {
            return inverseAsEasternCountry(value);
        } else if(sysLanguage.equals(englishLanguage)) {
            return inverseAsWesternCountry(value);
        } else {
            return inverseAsEasternCountry(value);
        }
    }

    abstract protected String convertAsEasternCountry(double value);
    abstract protected String convertAsWesternCountry(double value);
    abstract protected double inverseAsEasternCountry(String value);
    abstract protected double inverseAsWesternCountry(String value);
}

 

위와 같이 LocaleItemFormatter라는 abstract class가 있습니다. convertinverse라는 템플릿 메서드(Template method)도 있습니다. 이 템플릿 메서드(Template method)에서는 국가별로 출력해 주어야 할 단위(kg -> lbs, cm -> feet)가 다릅니다. 한국과 일본, 중국의 경우는 동양권 나라 이기 때문에 모두 'kg', 'cm' 단위를 사용 합니다. 하지만, 미국의 경우는 'lbs', 'feet' 단위를 사용 합니다. 따라서 템플릿 메서드(Template method)에서 이 기본 골격의 대한 분기 로직을 구현 하였습니다. 이때, convertAsEasternCountry, convertAsWesternCountry 와 같은 추상 메서드(abstract method)를 호출 하여 구현 합니다. 이제, 실제로 동양권, 서양권 국가별 단위 연산은 구현 클래스에게 맡깁니다.

 

 

class WeightItemFormatter extends LocaleItemFormatter {
    private static final double POUND_TO_KG = 2.2046;

    WeightItemFormatter(Locale locale) {
        super(locale);
    }

    @Override
    protected String convertAsEasternCountry(double kg) {
        return String.format("%dkg", (int) kg);
    }

    @Override
    protected String convertAsWesternCountry(double kg) {
        int pound = (int) (kg * POUND_TO_KG);
        return String.format("%dlbs", pound);
    }

    @Override
    protected double inverseAsEasternCountry(String kg) {
        final String numberValue = kg.replaceAll(onlyNumberPattern.pattern(), "").trim();
        return Double.valueOf(numberValue);
    }

    @Override
    protected double inverseAsWesternCountry(String pound) {
        final String numberValue = pound.replaceAll(onlyNumberPattern.pattern(), "").trim();
        double numberPound = Double.valueOf(numberValue);
        return (int)(numberPound / POUND_TO_KG);
    }
}

 

convertAsEasternCountry, convertAsWesternCountry, inverseAsEasternCountry, inverseAsWesternCountry 와 같은 메서드를 동양권 나라서양권 나라의 맞게 변환 하는 로직을 구현해 줍니다. WeightItemFormatter는 LocaleItemFormatter 추상 클래스(abstract class) 를 상속 받습니다. cm <-> feet 변환 하는 로직에만 집중 합니다.

 

 

class HeightItemFormatter extends LocaleItemFormatter {
    private static final double CENTI_TO_INCH = 0.3937;
    private static final int INCH_TO_FEET = 12;

    HeightItemFormatter(Locale locale) {
        super(locale);
    }

    @Override
    protected String convertAsEasternCountry(double value) {
        return String.format("%dcm", (int)value);
    }

    @Override
    protected String convertAsWesternCountry(double centi) {
        double inches = CENTI_TO_INCH * centi;
        int feet = (int)(inches / INCH_TO_FEET);
        int leftover = (int)(inches % INCH_TO_FEET);
        return String.format("%dft %din", feet, leftover);
    }

    @Override
    protected double inverseAsEasternCountry(String centi) {
        final String numberValue = centi.replaceAll(onlyNumberPattern.pattern(), "").trim();
        return Integer.valueOf(numberValue);
    }

    @Override
    protected double inverseAsWesternCountry(String feetAndInches) {
        int feetIndex = feetAndInches.indexOf("ft");
        int inchIndex = feetAndInches.indexOf("in");

        int feet = Integer.valueOf(feetAndInches.substring(0, feetIndex));
        int inches = Integer.valueOf(feetAndInches.substring(feetIndex + 3, inchIndex));
        int inchesValue = (feet * INCH_TO_FEET) + inches;
        return (int)(inchesValue / CENTI_TO_INCH);
    }
}

 

convertAsEasternCountry, convertAsWesternCountry, inverseAsEasternCountry, inverseAsWesternCountry 와 같은 메서드를 동양권 나라와 서양권 나라의 맞게 변환 하는 로직을 구현해 줍니다. HeightItemFormatter는 LocaleItemFormatter 추상 클래스(abstract class) 를 상속 받습니다. kg <-> lbs 변환 하는 로직에만 집중 합니다.

 

 

3. 장점

이와 같이 템플릿 메서드(Template Method) 디자인 패턴을 사용 하므로써 장점은

1. 전체적으로 동일한 코드 흐름에서 동일한 부분의 코드의 중복을 줄일수 있습니다.

2. 같은 기능을 상위 클래스에서 정의 하면서 확장과 변화필요한 부분만 서브 클래스로 구현 할 수 있습니다.

 

 

4. 단점

1. 추상 클래스의 템플릿 메서드 외 다른 곳에서 구현 클래스의 메서드를 호출하지 않도록 해야 한다.

2. 구현해야 할 추상 메서드(abstract method)가 너무 많으면 관리하기 힘들 수 있습니다.

 

 

5. java의 대표적인 템플릿 메서드 패턴 예

1. java.util.Collections::sort()

2. java.util.AbstractList::indexOf()

3. java.io.InputStream::read()

반응형