목차
문제상황과 람다 탄생 과정
재고 관리 애플리케이션을 만드려고한다.
농부는 녹색사과를 찾는 기능만 있으면 된다고했다.
하루 지나니 150그램 이상인 사과를 찾는 기능도 있어야한다고한다.
또 하루 지나니 150그램 이상이면서 빨간 사과를 찾는 기능이 있어야한다고한다.
사용자의 요구에 유연한 애플리케이션을 만드려면 어떻게 해야할까?
1번째 시도 : 계속 변화하는 메서드
요청1) 녹색사과 필터링 메서드 만들어주세요
응답1) 네~
public List<Apple> filterGreenApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if(GREEN.equals(apple.getColor())){
result.add(apple);
}
}
return result;
}
요청2) 빨간사과 필터링 메서드 만들어주세요
응답2) 네~ (녹색사과 필터링 메서드를 복사&붙여넣고 GREEN을 RED로 바꿔서 만들어야겠다!)
public List<Apple> filterRedApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if(RED.equals(apple.getColor())){
result.add(apple);
}
}
return result;
}
요청3) 옅은 녹색 사과 필터링 메서드 만들어주세요
응답3) 음.. 색이 변할때마다 필터링 메서드를 만들수는 없는데... 가만보니 코드도 red와 green이 큰 차이가 없네..
2번째 시도 : 속성 파라미터화
아! 색을 파라미터화 해야겠당~~~ filterApplesByColor 생성
사용자 요청 시 : filterApplesByColor(RED) or filterApplesByColor(GREEN)....
----------------------------------------------------------------------------------
public List<Apple> filterApplesByColor(List<Apple> inventory, Color color){
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if(apple.getColor().equals(color)){
result.add(apple);
}
}
return result;
}
요청4) 아.. 쓰다보니 무게로도 필터 기능이 있어야할 것 같아요
응답4) 아..ㄴ..ㅔ..... filterApplesByWeight 생성
사용자 요청 시 : filterApplesByWeight(100) or filterApplesByWeight(200)....
----------------------------------------------------------------------------------
public List<Apple> filterApplesByWeight(List<Apple> inventory, int weight){
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if(apple.getWeight() > weight){
result.add(apple);
}
}
return result;
}
3번째 시도 : 모든 속성 파라미터화
현재는.. 사과의 수많은 속성 중 색깔, 무게 2가지로만 필터링을 했지만 나중에 원산지, 당도 등으로 필터링 하게된다면 어떡하지?
아! 사과의 모든 속성을 필터링할 수 있게 하나의 메서드로 합치면되나?!
사용자 요청 시 : filterApples(inventory, GREEN, 0, true), filterApples(inventory, null, 150, false)...
------------------------------------------------------------------------------------------------------
public List<Apple> filterApples(List<Apple> inventory, Color color, int weight, boolean flag){
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if( (apple.getWeight() > weight)||(apple.getColor() > color) ){
result.add(apple);
}
}
return result;
}
"filterApples(inventory, GREEN, 0, true)", "filterApples(inventory, null, 150, false)"
=> 코드를 다른사람이 봤을 때 어떤 의미인지 한눈에 알아볼 수 있을까? 절대 아니요..
절대 절대 엔티티의 속성을 파라미터로한 메서드를 만들지 말자!
4번째 시도 : 인터페이스와 조건구현클래스
사용자가 필요한 속성이 시시각각 변하는 상황에서, 개발자가 속성을 지정하는 것은 한계가 있다.
차라리 조건이 동작하는 메서드를 인터페이스로 만들어 구현하면 어떨까?
// true, false를 반환하는 함수를 predicate(프레디케이트)라고 한다.
public interface ApplePredicate{
boolean test (Apple apple);
}
-------------------------------------------------------------------------------
//ApplePredicate를 구현한 AppleWeightPredicate
public class AppleWeightPredicate implements ApplePredicate{
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
-------------------------------------------------------------------------------
//ApplePredicate를 매개변수로 받는 필터 메서드
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate predicate){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory){
if( predicate.test(apple) ){ //predicate로 사과를 검사한다.
result.add(apple);
}
}
return result;
}
-------------------------------------------------------------------------------
//필터 메서드 사용
filterApples(inventory, new AppleWeightPredicate());
ApplePredicate 인터페이스를 구현한 AppleWeightPredicate를 필터에 적용시켜서 유연한 코드를 만들 수 있다.
이제 어떤 조건이 와도 ApplePredicate를 구현한 클래스를 만들면된다!
하지만.. 조건이 100가지이면 ApplePredicate를 구현한 100가지 클래스를 만들어야되겠네?
5번째 시도 : 익명클래스
사실상 "코드"를 전달하는 것인데 불필요하게 ApplePredicate의 구현클래스를 생성하는 것은 아닐까??
구현클래스를 계속 생성하지말고, 익명클래스를 사용해보자.
// true, false를 반환하는 함수를 predicate(프레디케이트)라고 한다.
public interface ApplePredicate{
boolean test (Apple apple);
}
-------------------------------------------------------------------------------
//ApplePredicate를 구현한 AppleWeightPredicate 삭제
-------------------------------------------------------------------------------
//ApplePredicate를 매개변수로 받는 필터 메서드
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate predicate){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory){
if( predicate.test(apple) ){ //predicate로 사과를 검사한다.
result.add(apple);
}
}
return result;
}
-------------------------------------------------------------------------------
//필터 메서드 사용
filterApples(inventory, new ApplePredicate() {
public boolean test(Apple apple) { //기존 AppleWeightPredicate의 test를 구현
return apple.getWeight() > 150;
}
});
기존의 AppleWeightPredicate를 구현하지 않고, 익명클래스로 구현해서 동작시킬수도 있다.
하지만 익명클래스는 코드 읽기가 불편하다는 단점을 가지고 있다.
6번째 시도 : 람다 표현식 사용
드디어 람다를 사용한다! 6가지의 시도 중 제일 깔끔하고 코드 읽기가 편해졌다.
//ApplePredicate가 아닌 자바8에서 기본적으로 제공하는 Predicate를 사용한다.
//인터페이스명만 다를뿐이다.
public interface Predicate<T> {
boolean test(T t);
}
-------------------------------------------------------------------------------
//ApplePredicate를 구현한 AppleWeightPredicate 삭제
-------------------------------------------------------------------------------
//ApplePredicate를 매개변수로 받는 필터 메서드
public static List<Apple> filterApples(List<Apple> inventory, Predicate predicate){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory){
if( predicate.test(apple) ){ //predicate로 사과를 검사한다.
result.add(apple);
}
}
return result;
}
-------------------------------------------------------------------------------
//필터 메서드 사용
filterApples(inventory, (Apple apple) -> apple.getWeight() > 150);
6가지 코드 경우의 수를 보면서 람다가 왜 탄생했는지 알게되었다.
메서드의 인자로 코드를 전달하는 기법의 강력함을 알게되었다.
실전예제로 Comparator를 구현해보자
public interface Comparator<T>{
int compare(T o1, T o2);
}
//익명클래스 이용
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
});
//람다 이용
inventory.sort(
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
);
익명클래스와 람다를 비교해보자. 어떤 코드가 한눈에 들어올까? 당연히 람다다!
'Backend' 카테고리의 다른 글
모던 자바 인 액션 - Stream(스트림)이란?, 스트림특징, 내부반복/외부반복, 게으른중간연산 (0) | 2022.07.11 |
---|---|
모던 자바 인 액션 - 람다표현식, 함수형인터페이스 (1) | 2022.07.10 |
Spring DI(의존주입), @Autowired (의존 자동주입) (0) | 2022.04.02 |
Spring DI(의존주입), Container(객체 조립) (0) | 2022.03.28 |
Spring Container(ApplicationContext)와 Bean (0) | 2022.03.28 |