모던 자바 인 액션 (세션 2)

kimseungki·2022년 6월 16일
0

독서

목록 보기
4/8
post-thumbnail

개요

소비자의 요구조건은 항상 바뀐다. 예시로 빨간색을 처음엔 원했다가, 파란색도 원하고 무게도 원하는 등 소비자의
요구가 변하는 것에 맞는 유지보수를 고려한 코드를 작성해야 한다.

동적파라미터

동적파라미터는 코드블록에 따라 메서드 및 동작이 파라미터화 된다. 이 점에서 우리는 동적이라고 정의할 수 있다. 코드블록은 파라미터
를 말하고, 해당 파라미터는 추후 프로그램에서 호출이 된다.

1번 시도 (메서드 추가)

기존에 빨간색 사과를 원한다면, 빨간색 사과를 위한 메서드를 만들고, 파란색 사과가 있을경우 파란색 사과를 위한 메서드를 호출한다.

enum Color {RED, BLUE}
public static List<Apple> findRedApple(List<Apple> store) {
	List<Apple> list = new ArrayList<>();
    for(Apple apple: inventory) {
    	if(GREEN.equals(apple.getColor()) list.add(apple);
    }
}
public static List<Apple> findBlueApple(List<Apple> store) {
	List<Apple> list = new ArrayList<>();
    for(Apple apple: inventory) {
    	if(BLUE.equals(apple.getColor()) list.add(apple);
    }
}
....

해당 메서드를 호출을 할 경우 당장은 정말 좋은 코드일 수(?) 있지만.. 나중에 요구조건이 생길 때마다, 메소드를 계속 만들어야 된다.
그렇게 되면.. 메서드 이름을 어떻게 할지에 대한 고민 + 저런 메소드가 몇천개가 된다면..
뭘 써야될지 페닉이 오는 경우가 있다.(여담 : 전에 비슷한 경험을 했습니다... 선언도 문제지만.. 저거 수정하는게 더 심각한 문제다.)

2번 시도 (파라미터 추가)

기존에 색깔을 원하면 색깔을 파라미터에 넣고, 무게를 원할 경우 무게를 파라미터에 넣는다.

enum Color {RED, BLUE}
public static List<Apple> findColorApple(List<Apple> store, Color color) {
	List<Apple> list = new ArrayList<>();
    for(Apple apple: inventory) {
    	if(color.equals(apple.getColor()) list.add(apple);
    }
}
public static List<Apple> findBlueApple(List<Apple> store, int weight) {
	List<Apple> list = new ArrayList<>();
    for(Apple apple: inventory) {
    	if(weight < apple.getWeight()) list.add(apple);
    }
}
....

해당 메서드는 장점은 1뎁스(색깔, 무게) 2뎁스(파랑, 빨강, 가볍다, 무겁다 등)이 있다면 1뎁스에서 메소드 호출이 끝난다는 점이 1번보다
장점이다. 하지만 결국 메소드를 여러개를 만드는 것은 똑같고 이는 DRY(don't repeat yourself : 반복 X) 원칙에 어긋난다.

3번 시도 (boolean type 추가)

파라미터 중 boolean flag를 넣고, true : 색깔, false : 무게로 정의한 뒤(개발자 스스로) 실행

enum Color {RED, BLUE}
public static List<Apple> findApple(List<Apple> store, boolean flag) {
	List<Apple> list = new ArrayList<>();
    for(Apple apple: inventory) {
    	if(flag && color.equals(apple.getColor()) list.add(apple);
    	else if(!flag && weight < apple.getWeight()) list.add(apple);
    }
}

flag가 뭔지 알기위해선 타 개발자들은 아마 코드를 내부 깊숙히 보게 될 것이다. 또한 색깔, 무게 더 나아가 재질 이런 특성도 있다면
boolean의 한계를 느끼게 될 것이다.(여담 : 예전에 많이했다....)

4번 시도 (동적파라미터 : 추상적 조건으로 필터링)

코드블록 선언 시, 선언한 코드블록에 따라 메서드가 다르게 동작하게끔 설정이 된다.
전략 디자인 패턴 : 각 알고리즘을 캡슐화하는 알고리즘 패밀리를 정의해둔 다음, 런타임에 알고리즘을 선택하는 기법

public interface ApplePredicate() {
	void test();
}
public class AppleHeavyWeightPredicate implements ApplePredicate {
	public boolean test(Apple apple) {
    	return apple.getWeight() > 150;
    }
}
public class AppleGreenColorPredicate implements ApplePredicate {
	public boolean test(Apple apple) {
    	return GREEN.equels(apple.getColor());
    }
}
public static List<Apple> filterApples(List<Apple> store, ApplePredicate p){
	List<Apple> list = new ArrayList<>();
    for(Apple apple : inventory) {
    	if(p.test(apple){
        	list.add(apple);
        }
    }
}
List<Apple> appleGreenColorPredicate = filterApples(store, new AppleGreenColorPredicate);
List<Apple> appleHeavyWeightPredicate = filterApples(store, new AppleHeavyWeightPredicate);

이런 식으로 filterApples에 선언한 코드블록(AppleGreenColorPredicate)이 다를경우 filterApples 메서드에 동작은 다르게 작동된다.
하지만, 선언과 인스턴스 생성을 위한 코드를 따로 해야된다는 점이 유지보수성에서 조금 아쉬운 점이 있다.

5번 시도 (익명 클래스)

익명클래스의 장점은 클래스 선언과 인스턴스를 동시에 생성할 수 있다.

List<Apple> appleGreenColorPredicate = filterApples(store, new AppleGreenColorPredicate(){
	public boolean test(Apple apple) {
    	return GREEN.equels(apple.getColor());
    }
});

이렇게 만들게 된다면, 코드가 로직이 길어지면 상당히 장황스럽고, 너무 길어서 가독성이 떨어질 것이다.

6번 시도 (람다 표현식 사용)

List<Apple> appleGreenColorPredicate = 
	filterApples(store, (Apple apple) -> GREEN.equels(apple.getColor()));

선언과 인스턴스 생성을 동시에 하면서, 코드 역시 간결하게 작성할 수 있다. 자세한건 추후 3장에서 확인해보자.

7번 시도 (리스트 형식으로 추상화)

제네릭을 통해 타입을 선언을 함으로써 다양한 객체가 들어올 때를 대비할 수 있다.

public static <T> 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;
}
List<Apple> appleGreenColorPredicate = 
	filterApples(store, (Apple apple) -> GREEN.equels(apple.getColor()));
List<Banana> appleGreenColorPredicate = 
	filterApples(store, (Banana banana) -> GREEN.equels(banana.getColor()));  
List<Banana> appleGreenColorPredicate = 
	filterApples(store, (Integer i) -> i%2==0;

이런식으로 추후 사과뿐만아니라 다른과일을 선택할 때도 고려할 수 있는 형식이다.

의문

람다의 사용과 어떤방식으로 작동이 되는지는 아직 잘 모르겠다. 3장에서 확인을 해봐야겠다.

후기

  1. 동적파라미터가 어떤건지 왜 쓰는지를 여러 시도를 보면서 이해할 수 있었다.
  2. 람다에 대해 더 자세하게 알고싶다.
profile
seung 기술블로그

0개의 댓글