소비자의 요구사항은 항상 바뀐다. 이런 변화하는 요구사항에 대해 효과적으로 대응하기 위해서 동작 파라미터화(behavior parameterization) 를 이용하면 좋다.
영화 관련 객체를 필터링하는 예시
자바 인 액션 책의 예제와 비슷한 예제로 해보았다.
public class Movie {
private String name;
private String director;
private Genre genre;
private int rating;
// getters and setters omitted...
}
public static List<Movie> filterMoviesByRating(List<Apple> movies, int rating) {
List<Movie> result = new ArrayList<>();
for(Movie movie : movies) {
if(movie.getRating() > rating) {
result.add(movie);
}
}
return result;
}
절대 사용하지 말아야 하는 방법
public static List<Movie> filterMovies(List<Apple> movies,int rating, Genre genre, boolean flag) {
List<Movie> result = new ArrayList<>();
for(Movie movie : movies) {
if((flag && movie.getRating() > rating) ||
(!flag && movie.getGenre().equals(genre)){
result.add(movie);
}
}
return result;
}
List<Movie> TopRatedMovies = filterMovies(movies, 4, null, true);
List<Movie> horrorMovies = filterMovies(movies, 4, HORROR, false);
기존까지는
값을 파라미터화
했다면 요구사항 변화에 유연하게 대응하도록동작을 파라미터화
를 사용해보자!
public interface MoviePredicate {
boolean test(Movie movie);
}
public class TopRatedMoviePredicate implements MoviePredicate {
public boolean test(Movie movie) {
return movie.getRating() > 4;
}
}
public class HorrorMoviePredicate implements MoviePredicate {
public boolean test(Movie movie) {
return movie.getGenre().equals(HORROR);
}
}
public class MovieDirectorPredicate implements MoviePredicate {
private String director;
public MovieDirectorPredicate(String director) {
this.director = director;
}
public boolean test(Movie movie) {
return movie.getDiretor().equals(director);
}
}
전략 디자인패턴(strategy design patter)
이라고 부른다.public static List<Movie> filterMovies(List<Apple> movies, MoviePredicate p) {
List<Movie> result = new ArrayList<>();
for(Movie movie : movies) {
if(p.test(movie)) {
result.add(movie);
}
}
return result;
}
List<Movie> horrorMovies = filterMovies(movies, new HorrorMoviePredicate());
List<Movie> bongMovies = filterMovies(movies, new MovieDirectorPredicate("Bong"));
클래스의 선언과 인스턴스화
를 동시에 수행할 수 있도록 익명 클래스
를 이용하여 개선 가능하다.클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)
List<Movie> TopRatedMovies = filterMovies(movies, new MoviePredicate() {
public boolean test(Movie movie) { // but 여기 까지 필요없는 코드
return movie.getRating() > 4;
}
});
List<Movie> horrorMovies = filterMovies(movies, new MoviePredicate() {
public boolean test(Movie movie) { // but 여기 까지 필요없는 코드
return HORROR.equals(movie.getGenre());
}
});
List<Movie> bongMovies = filterMovies(movies, new MoviePredicate() {
public boolean test(Movie mioive) { // but 여기 까지 필요없는 코드
return "Bong".equals(movie.getDirector())
}
});
MoviePredicate는 함수형 인터페이스이므로 람다 표현식 사용 가능하다.
List<Movie> horrorMovies = filterMovies(movies, (Movie m) -> HORROR.equals(movie.getGenre());
List<Movie> bongMovies = filterMovies(movies, (Movie m) -> "Bong".equals(movie.getDirector());
public interface Predicate<T> {
boolean test(T t);
}
public static List<T> filter(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>();
for(T e : list) {
if(p.test(e)) {
result.add(e);
}
}
return result;
}
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}