# Strategy Pattern 스벤 토리(Sven Tori)는 사용자 목록을 담은 페이지를 보는 데 많은 돈을 쓴다. 그런데 사용자는 이름순으로 정렬되어 있어야 하고, 유료 사용자들은 다른 사용자들보다 앞에 나타나야 한다. 그 이유는 돈을 지불하기 때문이다. 역순으로 정렬할 때에도 유료 사용자들은 앞에 나열되어야 한다. 페드로: 아! 커스텀 비교자(custom comparator)를 제공해서 Collectoins.sort(user, comparator)를 호출하면 되겠네요. 이브: 커스텀 비교자는 어떻게 구현하시려고요? 페드로: Comparator 인터페이스를 이용해 compare(Object o1, Object o2) 메소드를 구현하면 되요. ReverseComparator 클래스의 경우에도 마찬가지로 Comparator 인터페이스를 구현하면 됩니다. 이브: 말보다는 코드로 보여 주세요! class SubsComparator implements Comparator { @Override public int compare(User u1, User u2) { if (u1.isSubscription() == u2.isSubscription()) { return u1.getName().compareTo(u2.getName()); } else if (u1.isSubscription()) { return -1; } else { return 1; } } } class ReverseSubsComparator implements Comparator { @Override public int compare(User u1, User u2) { if (u1.isSubscription() == u2.isSubscription()) { return u2.getName().compareTo(u1.getName()); } else if (u1.isSubscription()) { return -1; } else { return 1; } } } Collections.sort(users, new SubsComparator()); Collections.sort(users, new ReverseSubsComparator()); 페드로: 클로저로는 어떻게 할 수 있죠? 이브: 예, 다음과 같이 합니다. (sort (comparator (fn [u1 u2] (cond (= (:subscription u1) (:subscription u2)) (neg? (compare (:name u1) (:name u2))) (:subscription u1) true :else false))) users) 페드로: 아주 유사하네요 이브: 하지만 더 간단하게 할 수도 있어요 ;; forward sort (sort-by (juxt (complement :subscription) :name) users) ;; reverse sort (sort-by (juxt :subscription :name) #(compare %2 %1) users) 페드로: 세상에! 달랑 한 줄짜리 코드네요 이브: 보시다시피, 그냥 함수들일 뿐이죠 페드로: 어쨋거나, 코드를 이해하기는 매우 어렵네요. 이브는 [[juxt]]와 [[complement]], [[sort-by]] 함수에 대해 설명한다. 10분이 흐른 뒤에 페드로: 전략 패턴 자체를 넘어서는 아주 이상한 방식이네요 이브: 상관 없어요. 전략 패턴은 단순히 어떤 함수에 인수로 전달되는 함수일 뿐이니까요. (def users [{:name "Bob", :subscription :premium} {:name "Alice", :subscription nil} {:name "Eve", :subscription :premium} {:name "Carol", :subscription nil} {:name "Dave", :subscription :premium} {:name "Frank", :subscription nil}]) (sort-by (juxt (complement :subscription) :name) users) ;=> ({:name "Bob", :subscription :premium} {:name "Dave", :subscription :premium} {:name "Eve", :subscription :premium} {:name "Alice", :subscription nil} {:name "Carol", :subscription nil} {:name "Frank", :subscription nil})