[모던 자바 인 액션] Chapter 2 동작 파라미터화 코드 전달하기

OhJuYeong·2025년 12월 16일

모던 자바 인 액션

목록 보기
2/9
post-thumbnail

장기적인 관점에서 유지보수가 쉬워야 함

동작 파라미터화를 이용하면 자주 바뀌는 요구사항에 효과적으로 대응할 수 있음

동작 파라미터화

  • 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록
  • 나중에 호출

2.1 변화하는 요구사항에 대응하기

변화에 대응하는 코드를 구현하는 것은 어려운 일

모범 사례

기존의 농장 재고 목록 애플리케이션에 리스트에서 녹색 사과만 필터링 하는 기능 추가 한다고 가정

2.1.1 첫번째 시도 : 녹색 사과 필터링

enum Color{RED,GREEN}

public static 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;	
}

이렇게 녹색 사과만 필터링을 할 수 있음

근데 갑자기 빨간색 사과를 필터링 하고 싶게 되면 메서드를 복사해서 새로운 메서드를 만들고 if 문의 조건을 빨간사과르 바꾸는 방법을 선택 할 수 있음

하지만 더 다양한 색을 필터링 할때 적절하게 대응 할 수 없음

→ 이렇게 반복될때는 코드를 추상화하는 좋은 규칙이 있음

2.1.2 두번 째 시도: 색을 파라미터화

public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color)
{
	List<Apple> result = new ArrayList<>();
	for ( Apple apple: inventory){
		if ( apple.getColor().equal(color)){
			result.add(apple);
		}
	}
	return result;
}

이렇게 하면

List greendApples = filterApplesByColor(inventory , GREEN);

이런식으로 호출 할 수 있음

하지만 이때 무게로도 판단하고 싶다고 하면 또 다시 메서드를 복사하고 조건만 수정해 중복된 메서드가 생길 것

2.1.3 세번째 시도: 가능한 모든 속성으로 필터링

public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color, int weight, boolean flag))
{
	List<Apple> result = new ArrayList<>();
	for ( Apple apple: inventory){
		if ((flag && appel.getColor().equals(color)) || 
			(!gloag && apple.getWeigth() > weight){
			result.add(apple);
		}
	}
	return result;
}

이렇게 되면 형편 없는 코드가 됨

이걸 해결하기 위해 동작 파라미터화를 이용

2.2 동작 파라미터화

참 거짓을 반환하는 함수를 프레디케이트

선택 조건을 결정하는 인터페이스 정의

public interface ApplePredicate{
	boolean test (Apple apple);
}

public class AppleHeavyWeightPredicate implements ApplePredicate{
	public boolean test(Apple apple){
		return apple.getWegith()>150;
	}
}

조건에 따라 filter 메서드가 다르게 동작할 것이라고 예상

→ 이를 전략 디자인 패턴

전략 디자인 패턴

  • 각 알고리즘을 캡슐화하는 알고리즘 패밀리를 정의해둔 다음에 런타임에 알고리즘을 선택하는 기법
  • ApplePredicate가 알고리즘 패밀리 AppleHeavyWeightPReciate 전략

ApplePredicate는 어떻게 다양한 동작 수행?

  • filterApples엣거 ApplePredicate 객체를 받아 애플의 조건을 검사하도록 메서드 수정 필요
  • 동작 파라미터화
    • 메서드가 다양한 동작을 받아서 내부적으로 다양한 동작을 수행

filterApples메서드가 ApplePredicate 객체를 인수로 받도록 수정

  • filterApples 메서드 내부에서 컬렉션을 반복하는 로직과 컬렉션의 각 요소에 적용할 동작을 분리할 수 있다는 점에서 이득

2.2.1 네번째 시도: 추상적 조건으로 필터링

public static List<Apple> filterApples(List<Apple> inventory,ApplePrdicate p)
{
	List<Apple> result = new ArrayList<>();
	for ( Apple apple: inventory){
		if ( p.test(apple){ //프레디케이트 객체로 사과 검사 조건을 캡슐화
			result.add(apple);
		}
	}
	return result;
}

이제 필요한 대로 다양한 ApplePredicate를 만들어서 filterApples 메서드로 전달 가능

ex ) 150 그램이 넘는 빨간 사과를 검색해달라고 부탁

public class AppleRedAndHeavyPredicate implements ApplePredicate{
	public boolean test(Apple apple){
		return RED.equlas(apple.getColor())
			&& apple.getWegith()> 150;
	}
}

한개의 파라미터, 다양한 동작

컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 잇다는 것이 동작 파라미터화 의 강점

2.3 복잡한 과정 간소화

위에처럼 한다면 로직과 관련없는 코드가 많이 추가 됨

자바에서는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스 기법 제공

  • 모든것을 해결하진 않지만 코드 양 줄일 수 잇음

2.3.1 익명 클래스

익명 클래스

  • 자바의 지역 클래스와 비슷한 개념
  • 이름이 없는 클래스
  • 클래스 선언과 인스턴스화를 동시에 할 수 있다.

2.3.2 다섯번째 시도 익명 클래스 사용

List<Apple> redApples = filterApples(inventory, new ApplePredicate(){
	public boolean test(Apple apple){
		return RED.equals(apple.getColor());
	}
});

부족한점

  1. 익명클래스는 여전히 많은 공간 차지
  2. 익명 클래스의 사용에 익숙하지 않음

2.3.3 여섯 번째 시도: 람다 표현식 사용

List<Apple> result = filterApples(inventory, (Apple apple) -> RED.equals(apple.getColor()));

2.3.4 일곱 번째 시도: 리스트 형식으로 추상화

public interface Predicate<T>{
 boolean test(T t);
}

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;
}

이제 바나나, 오렌지 , 정수, 문자열등의 리스트에 필터 메서드 사용 가능

2.4 실전 예제

2.4.1 Comparator 로 정렬

컬렉션 정렬은 반복되는 프로그래밍 작업

자바 8 List 에는 sort 기능 포함

java.util.Comparator 객체를 이용해서 sort의 동작 파라미터화

//java.util.Comparator
public interface Comparator<t>{
 int c ompar(T o1, To2);
}

sort 메서드의 동작 다양화

ex) 익명 클래스를 이용해서 무게가 적은 순서로 목록에서 사과를 정렬

inventory.sort(new Comparator<Apple>(){
 public int compare(Apple a1, Apple a2){
	 return a1.getWright().commjpareTo(a2.getWeight());
 }
});
//람다 사용하면
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

2.4.2 Runnable로 코드 블록 실행하기

자바 스레드를 이용하면 병렬로 코드 블록을 실행할 수 있음

자바 8까지는 Thread 생성자에 객체만을 전달 할 수 있었으므로 보통 결과를 반환하지 않는 void run 메소드를 포함하는 익명 클래스가 Runnable 인터페이스를 구현 하도록 하는 것이 일반적

public interface Runnable{
 void run();
}

Thread t = new Thread(new Runnable(){
 public void run()}{
  System.out.println("Hello world");
  }
 });
 
 //람다
 Thread t= new Thread(() -> Sysmtem.out.println("Hello World"));

2.4.3 Callable 을 결과로 반환하기

ExecutorService 인터페이스

  • 태스크 제출과 실행 과정의 연관성을 끊어줌
  • 태스트를 스레드 풀로 보내고 결과를 Future로 저장
  • Runnable 의 업그레이드 버전

2.4.4 GUI 이벤트처리

변화에 대응할 수 있는 유연한 코드 필요

모든 동작에 반응 할 수 있기 때문

profile
기록하는 개발자

0개의 댓글