본문 바로가기
독서/모던 자바 인 액션

동작 파라미터화2

by kriorsen 2023. 9. 24.

Predicate란?

참 또는 거짓을 변환하는 함수

1. 변화하는 요구사항에 대응하는 법

1.3 선택 조건을 결정하는 프레디케이트 정의

public interface BookPredicate { 
    boolean test(Book book); // boolean 값을 반환하는 메서드
}

이제 해당 인터페이스를 구현하는 다양한 프레디케이트를 정의할 수 있습니다.

public class BookThickPredicate implements BookPredicate { 
    public boolean test(Book book) {
        return book.getPage() >= 500;
       }
}

public class BookScienceCategoryPredicate implements BookPredicate { 
    public boolean test(Book book) {
        return Science.equals(book.getCategory());
       }
}

1.4 추상적 조건으로 필터링

public static List<Book> filterBooks(List<Book> bookshelf, BookPredicate bookPredicate) {
    List<Book> result = new ArrayList<>();
    for (Book book : bookshelf) {
        if (bookPredicate.test(book)) {
            result.add(book);
           }
    }
    return result;
}

이렇게 작성된 코드는 선생님의 모든 요구 사항에 유연하게 대응할 수 있고, 가독성 또한 flag를 이용한 방식보다 높아졌습니다. 만약 선생님께서 페이지가 500이 넘는 경제 카테고리의 책을 원한다면 BookPredicate를 구현하는 클래스만 작성해서 간단하게 필터링 작업을 수행할 수 있습니다.

public class BookEconomyCategoryAndThickPredicate implements BookPredicate { 
    public boolean test(Book book) {
        return Economy.equals(book.getCategory()) && book.getPage() >= 500;
       }
}

위에서 볼 수 있는 것처럼 하나의 메서드가 다른 동작을 수행할 수 있도록 하여, 정적인 데이터가 아닌 코드 자체를 전달 할 수 있는 것이 바로 동작 파라미터화의 큰 장점이라고 할 수 있습니다.

하지만 이렇게 유연한 코드에도 단점은 존재합니다. 필터링을 위해 여러 클래스를 구현해야 하고 로직과 관련 없는 코드가 많이 추가될 수 있다는 점인데요, 아래 코드를 보면 더 명확히 이 점을 확인할 수 있습니다.

public class BookThickPredicate implements BookPredicate { 
    public boolean test(Book book) {
        return book.getPage() >= 500;
       }
}

public class BookScienceCategoryPredicate implements BookPredicate { 
    public boolean test(Book book) {
        return Science.equals(book.getCategory());
       }
}

public class FilteringBooks {
    public static void main(String[] args) {
        List<Book> bookshelf = Arrays.asList(new Book(510, Science),
                                             new Book(100, Economy),
                                             new Book(120, Science));
       List<Book> scienceBooks = filterBooks(bookshelf, new BookScienceCategoryPredicate());
       List<Book> thickBooks = filterBooks(bookshelf, new BookThickPredicate());
    }

    public static List<Book> filterBooks(List<Book> bookshelf, BookPredicate bookPredicate) {
        List<Book> result = new ArrayList<>();
        for (Book book : bookshelf) {
            if (bookPredicate.test(book)) {
                result.add(book);
               }
        }
        return result;
    }
}

1.5 익명 클래스 사용하기

익명 클래스란?

이름이 없는 클래스로 선언과 인스턴스화를 동시에 할 수 있으며, 별도의 클래스 선언 없이 필요한 구현만 바로 만들어서 사용

자바에서는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스라는 기법을 제공하는데 이를 이용하면 코드의 양을 줄일 수 있습니다.

List<Book> scienceBooks = filterBooks(bookshelf, new BookPredicate() {
    public boolean test(Book book) {
        return Science.equals(book.getCategory());
       }
}); // 메서드 동작을 직접 파라미터화

코드를 별도로 정의하지 않아 코드의 양이 조금 줄어들었지만, 여러 익명 클래스를 구현하면 중복되는 코드는 여전히 있습니다. 그리고 메서드를 호출하는 코드가 장황해져서 가독성이 떨어졌다는 단점이 있습니다. 지난 장과 달리 이번 장에서는 실제로 동작을 파라미터화하는 것에 성공했으나, 코드의 가독성에 여전히 이슈가 있습니다. 다음 장에서는 이 파라미터화된 코드 로직을 더 간결하게 표현할 방법에 대해 알아보도록 하겠습니다.