ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 람다식이랑 조금 친해져보자!
    Java 2022. 8. 26. 02:35

    1. Intro

    중복된 코드를 어떻게 줄일 수 있을까를 고민하다 돌고돌고돌고돌아가서 해결은 했지만, 코드의 복잡성은 해결하지 못한 슬픈 코드가 있었습니다. 피드백 코드를 분석하면서 왜 그렇게 돌고 돌아갔는지를 깨닫게 되었습니다.

    람다식...! 람다식을 스트림 내에서 사용하는 것에만 익숙해져있었기 때문에 해결법을 떠올리지 못했습니다. 자바 8부터 도입된 람다식을 이제 메서드의 파라미터로 전달하는 연습을 해보도록 하겠습니다.

     

    2. 구현해야 되는 기능

    기준점이 되는 좌표가 있습니다.
    기준점과 x축이 같은 좌표와 y축이 같은 좌표를 추출해야합니다.

     A: (10 ,10)	<-- 기준점
     B: (22 ,10)	<-- A와 y축이 같다.
     C: (22 ,18)
     D: (10 ,18)	<-- A와 x축이 같다.

    기준점이 A 라면 A와 같은 x축을 가진 좌표는 D이고 같은 y축을 가진 좌표는 B입니다.

    3. Before 코드

    💡 리팩토링하기 전의 코드라 복잡합니다. 주요 부분만 봐주세요.

    3.1 중복되는 코드

    public class Coordinates {
        private final List<Point> points;
    
        ...
    
        public Point findPointToSameX(Point standard) {
            return points.stream()
                    .filter(point -> !standard.equals(point))  // <-- 자기 자신 제외
                    .filter(point -> standard.getX() == point.getX())   // <-- 여기!
                    .findFirst()
                    .orElseThrow(() -> new IllegalArgumentException("x축이 같은 좌표가 존재하지 않습니다."));
        }
    
        public Point findPointToSameY(Point standard) {
            return points.stream()
                    .filter(point -> !standard.equals(point))   // <-- 자기 자신 제외
                    .filter(point -> standard.getY() == point.getY())   // <-- 여기!
                    .findFirst()
                    .orElseThrow(() -> new IllegalArgumentException("y축이 같은 좌표가 존재하지 않습니다."));
        }
    }
    • A 좌표를 standard로 가정하겠습니다. (x축:10, y축:10)
    • findPointToSameX 메서드와 findPointToSameY 메서드는 두번째 filter() 메서드 부분을 제외하곤 동일한 코드입니다.
    .filter(point -> standard.getX() == point.getX()) 
    
    .filter(point -> standard.getY() == point.getY())

     

    3.2 호출하는 코드

    위 메서드를 호출하고 있는 코드를 살펴봅시다.

    private double extractHeight(Point standard) {
        Point sameX = points.findPointToSameX(standard);
        ...
    }
    
    private double extractWidth(Point standard) {
        Point sameY = points.findPointToSameY(standard);
        ...
    }

     

    4. After 코드

    위의 호출하는 코드와 아래의 중복된 코드를 비교해 봅시다.

    public Point findPointToSameX(Point standard) {
        //...
        .filter(point -> standard.getX() == point.getX())
        //...
    }
    
    public Point findPointToSameY(Point standard) {
        //...
        .filter(point -> standard.getY() == point.getY())
        //...
    }

    filter()의 파라미터로 넘겨준 람다식을 findPointToSameX() 메서드의 두번째 파라미터로 추가해보겠습니다. filter() 메서드의 파라미터 타입이 어떻게 정의되어 있는지 살펴봅시다.

     

    4.1 Stream<T> 인터페이스의 filter()

    public interface Stream<T> extends BaseStream<T, Stream<T>> {
    
        Stream<T> filter(Predicate<? super T> predicate);

    filter() 메서드의 파라미터로 Predicate<? super T> 타입을 전달하라고 정의되어 있습니다. 이제 findPointToSameX() 메서드의 두번째 파라미터로 Predicate<Point> 타입의 predicate를 전달해 봅시다.

    public Point findPointToSameX(Point standard, Predicate<Point> predicate) {
        //...
        .filter(predicate)
        //...
    }
    
    
    public Point findPointToSameY(Point standard, Predicate<Point> predicate) {
        //...
        .filter(predicate)
        //...
    }

    코드가 동일해졌으니 아래와 같이 하나의 메서드로 줄일 수 있습니다.

    public Point findSamePoint(Point standard, Predicate<Point> predicate) {
        //...
        .filter(predicate)
        //...
    }

     

    4.2 호출하는 코드

    private double extractHeight(Point standard) {
        Point sameX = points.findSamePoint(standard, point -> standard.getX() == point.getX());
        ...
    }
    
    private double extractWidth(Point standard) {
        Point sameY = points.findSamePoint(standard, point -> standard.getY() == point.getY());
        ...
    }



    5. Conclusion

    호출 하는 쪽에서 파라미터로 람다식을 전달하고, 호출 받는 쪽에서는 전달 받은 파라미터를 filter() 메서드의 파라미터로 전달하면 되는 간단한 문제였습니다.

    이해하고 나니, 1분 만에 리팩토링할 수 있었던 코드였다는 게 보이네요. 아는만큼 보인다더니...
    그래도 돌아간 만큼 와닿는 게 크니 다음 번엔 더 빨리 떠올릴 수 있을 것 같습니다.

    댓글