HihoBookStudy / EffectiveJava

이펙티브 자바 북스터디입니다.
1 stars 0 forks source link

[item19] 도우미 메서드(Helper Method) #28

Closed IAGREEBUT closed 2 months ago

IAGREEBUT commented 2 months ago

128page

클래스의 동작을 유지하면서 재정의 가능 메서드를 사용하는 코드를 제거할 수 있는 기계적인 방법을 소개한다. 먼저 각각의 재정의 가능 메서드는 자신의 본문 코드를 private '도우미 메서드'로 옮기고, 이 도우미 메서드를 호출하도록 수정한다. 그런다음 재정의 가능 메서드를 호출하는 다른 코드들도 모두 이 도우미 메서드를 직접 호출하도록 수정하면 된다.

라는 말이 나오는데 위의 상황에서 도우미 메서드 방식을 이용하여 문제점을 해결한 예시를 혹시 들어주실 수 있을까요?)

<추가> java의 helper method에 대해 구글링 해봤는데 이 게시글밖에 나오지 않아 가져왔습니다. https://teamtreehouse.com/community/what-exactly-is-a-helper-method

도우미 메서드는 여러 클래스들에서 공통적으로 반복되는 특정 작업을 수행하는데 사용됩니다. 이 메서드는 같은 내용의 코드를 서로다른 클래스에서 반복적으로 작성되는 것을 막아줍니다. static 메서드가 그 작업을 "정의"하는 반면에, 도우미 메서드는 과정을 보조하는 역할을 합니다. Integer.parseInt()를 도우미메서드로 생각해볼 수 있습니다. 우리는 이 메서드를 서로 무관한 다양한 클래스들에서 독립적인 작업들에 사용합니다.

헬퍼 메서드는 종종 public static으로 선언해서, 클래스 이름을 이용해 호출할 수 있습니다. (Classname.method() , integer.parseInt()). 아래의 예시로 설명하겠습니다. 여러 클래스에서 사용할 수 있는 헬퍼 메소드들로 구성된 헬퍼 클래스를 생성하였습니다. 테스트라는 클래스를 생성하여 해당 클래스에서 HelperMethods.preMr()라는 메소드를 이용해 사용자의 이름에 Mr를 붙여서 출력할 수 있도록 사용했습니다. 또한, generic을 이용하면 어떠한 프로젝트에서도 사용 가능한 커스텀 라이브러리로 사용될수도 있습니다.

public class HelperMethods {
     public static String preMr(String name) {
           return "Mr. "+name;
     }

     public static String preMs(String name) {
           return "Ms. "+name;
     }

     public static String preDr(String name) {
           return "Dr. "+name;
     }
}
public class Test {
     public static void main(String[] args) {
           String name = "Manav";
           System.out.println(HelperMethods.preMr(name)); // Mr. Manav
     }
}
ForteEscape commented 2 months ago

Q.

도우미 메서드 방식을 이용하여 문제점을 해결한 예시를 혹시 들어주실 수 있을까요?

A.

class TestSubClass extends TestClass {

    @Override
    public int add(int a, int b) {
        return a + a - b;
    }

@Override
    public int multiply(int a, int b) {
            System.out.println("a : " + a + " b : " + b);
            return super.multiply(a, b);
    }

public void printMultiplyResult(int a, int b) {
    multiply(a, b);
}

public void additionalMultiply(int a, int b, int c) {
    multiply(multiply(a, b), c);
}

}

public class HelperMethodTest {

public static void main(String[] args) {
    TestClass test = new TestSubClass();

    // do something with test...
}

}

- `TestClass`를 상속받은 `TestSubClass`는 `TestClass`에서 구현한 메서드를 사용하여 추가적인 기능들을 덧붙였습니다.
- TestSubClass에서 `multiply()` 메서드를 재정의했는데 내부적으로는 `add()` 메서드를 사용합니다. 그런데 `add()` 메서드도 `TestSubClass`에서 재정의되었으므로 재정의된 `add()` 메서드를 사용하게 됩니다.
    - 문제는 재정의된 `add()` 메서드가 곱셈 연산을 정의하는 `multiply()`에 사용되기에 적절하지 않다는 것입니다. 따라서 기대하던 값과는 전혀 다른 결과가 반환되게 됩니다.

- 이를 막기 위해서 재정의 가능한 코드(위 코드에서는 `add()`, `multiply()`, `pow()`가 될 것입니다.)를 사용하는 코드들을 제거하는 방식이 private helper method 입니다.
- 책에서 제시하는 방법을 따라가 봅시다. 먼저 각각의 재정의 가능한 메서드의 본문 코드를 private helper method로 옮긴다 부터 진행해 봅시다.
    - helper method는 질문에서 제시한 것 처럼 공통적으로 반복되는 특정 작업을 수행하는데 사용됩니다.
    - 위의 코드에서는 `add()`, `multiply()`가 공통적으로 반복되는 작업이 됩니다. 이를 helper method가 처리하도록 만들어주며, 외부에서 접근할 수 없도록 `private` 지정자를 붙여 주는 것입니다.
- 첫 step의 결과는 다음과 같습니다. 재정의 가능한 메서드들의 리턴 값이 없는 것은 의도한 것으로, 다음 step을 위해 비운 것입니다.

``` java
class TestClass {

    public TestClass(){}

    public int add(int a, int b) {

    }

    public int multiply(int a, int b) {

    }

    public int pow(int a, int b) {

    }

    private int addNumber(int a, int b) {
        return a + b;
    }

    private int multiplyNumber(int a, int b) {
        int res = 0;

        for (int i = 0; i < b; i++) {
            res += add(a, b);
        }

        return res;
    }

    private int powNumber(int a, int b) {
        int res = 0;

        if (b == 0 || a == 1) {
            return 1;
        }

        if (b == 1) {
            return a;
        }

        for (int i = 0; i < b - 1; i++) {
            res += multiply(a, a);
        }

        return res;
    }
}

// TestSubClass, HelperMethodTest Code...
class TestClass {

    public TestClass(){}

    public int add(int a, int b) {
        return addNumber(a, b);
    }

    public int multiply(int a, int b) {
        return multiplyNumber(a, b);
    }

    public int pow(int a, int b) {
        return powNumber(a, b);
    }

    private int addNumber(int a, int b) {
        return a + b;
    }

    private int multiplyNumber(int a, int b) {
        int res = 0;

        for (int i = 0; i < b; i++) {
            res += addNumber(a, b); // change method call
        }

        return res;
    }

    private int powNumber(int a, int b) {
        int res = 0;

        if (b == 0 || a == 1) {
            return 1;
        }

        if (b == 1) {
            return a;
        }

        for (int i = 0; i < b - 1; i++) {
            res += multiplyNumber(a, a); // change method call
        }

        return res;
    }
}

class TestSubClass extends TestClass {

        @Override
        public int add(int a, int b) {
            return a + a - b;
        }

    @Override
        public int multiply(int a, int b) {
                System.out.println("a : " + a + " b : " + b);
                return super.multiply(a, b);
        }

    public void printMultiplyResult(int a, int b) {
        multiply(a, b);
    }

    public void additionalMultiply(int a, int b, int c) {
        multiply(multiply(a, b), c);
    }

}

public class HelperMethodTest {

    public static void main(String[] args) {
        TestClass test = new TestSubClass();

        // do something with test...
    }
}