[자바 8-11] 자바에서 자바코드를 전달하는 방법, 동작 파라미터화

코린이서현이·2024년 12월 9일
0

Java

목록 보기
48/50
post-thumbnail

요구사항이 시도때도 없이 바뀌는 클라이언트가 있다면?

🧑‍🌾 사과를 선별해주세요! 색깔, 무게, 원산지로요!
🧑‍💻 네! ( 그렇다면 사과의 색깔, 무게, 원산지를 입력값으로 받는 메서드를 구현해야겠다.!)

🧑‍🌾 앗! 오늘은 색깔, 무게로만 선별하고 싶어요!
🧑‍💻 앗! 네! ( 그렇다면 사과의 색깔, 무게만 입력값으로 받는 메서드를 구현해야겠다.!)

🧑‍🌾 앗! 색깔, 무게, 원산지 그리고 하여튼..!!! 이거 다 해주세요 ! 또 요청할게요!! !! !!
🧑‍💻 아이고...

아래처럼 변경이 큰 상황에서는 어떻게 해야 유연한 설계를 할 수 있을까?

단순히 모든 경우에 대한 메서드를 구현한다면?

여러 코드가 중복될 것이다. 색깔로 선별하는 메서드, 색깔과 무게로 선별하는 메서드는 색깔로 선별하는 코드가 중복되게 될 것이다.

이렇데 단순히 복사-붙여넣기로 만든 코드는 다음과 같은 문제점을 초래할 수 있다.

  1. 수정이 어려워진다.
  2. 코드의 양이 늘어나 읽기 힘들어진다.
  3. 소프트웨어 공학의 DRY 원칙을 위배하게 된다.

결국 모든 상황과 변경에 대한 메서드를 만들어 놓는 것은 좋은 방법은 아니다.

위 상황은 선별하는 값이 중요한 것이 아니라 어떤 동작으로 선별할 것인지가 중요하다. 즉, 선별하는 동작의 정보를 메서드의 입력값으로 전달해야한다. 그리고 이것이 동작을 파라미터화 하는 방법이다.

동작을 파라미터화하는 것

동작을 메서드의 입력 값으로 전달할 수 있을까?

자바의 인터페이스를 활용하면 가능하다. 직접적으로 함수를 전달받을 수는 없지만, 동작을 미리 정해놓은 인터페이스를 이용해서 이를 구현할 수 있다.

즉, (1) 사과를 선별하는 추상메서드를 가진 인터페이스를 만들고, (2) 사과를 선별하는 메서드의 입력값으로 이 인터페이스를 받게 만든다.
그리고 이후에 (3) 구체적인 인터페이스의 구현체를 입력값으로 전달해, 유연한 설계를 만들 수 있다.

(1) 사과를 선별하는 추상메서드를 가진 인터페이스

interface ApplePredicate {
    boolean test(Apple apple);    // 사과를 선별하는 추상메서드
}

(2) 1번을 파라미터로 받는 메서드

public class AppleFilter {
    public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate predicate) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (predicate.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }
}

(3) 1번의 구현체

// 무거운 사과 선별
class HeavyApplePredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}

// 녹색 사과 선별
class GreenApplePredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return GREEN.equals(apple.getColor());
    }
}

(4) 사용

// 무거운 사과 선별
List<Apple> heavyApples = 
	AppleFilter.filterApples(inventory, new HeavyApplePredicate());

// 녹색 사과 선별
List<Apple> greenApples = 
	filter.filterApples(inventory, new GreenApplePredicate());

이런 구현방식을 표준화한 패턴을 전략 패턴이라고도 한다.

익명 클래스, 람다 표현식를 통해 조금 더 개선하기

위의 구현 방식을 통해 유연한 설계를 할 수 있다. 그러나 인터페이스, 구현체등 구현할 양이 많다. 조오오금 복잡하다! 그리고 사실 귀찮다!

이를 개선하기 위해서는 익명 클래스를 도입해볼 수 있다. 그리고 더 간단한 방법은 람다 표현식이다.

익명 클래스를 사용해보기

위에서 귀찮았던 점은, 클래스를 선언하고 인스턴스화하는 두번의 과정이었다.
익명 클래스는 클래스 선언과 인스턴스화를 한번에 한다.

(1)번과 (2)번의 과정은 동일하기 때문에 생략했다.

클래스 생성과 인스턴스화를 한번에 해보기

List<Apple> heavyApples = 
	AppleFilter.filterApples(inventory, new ApplePredicate() {
    	public boolean test(Apple apple) {
        	return apple.getWeight() > 150;
    	}
    });

람다 표현식을 사용해보기

그러나... 익명 클래스보다는 더 간단한 람다 표현식이 있다.

List<Apple> heavyApples = AppleFilter.filterApples(inventory,
    apple -> apple.getWeight() > 150);

인터페이스 구현 - 구현체 구현 - 사용의 복잡함 보다는 람다 표현식이 조금 더 간결하다.

결국, 동작을 전달하는 것의 유용성

동작을 전달하기 전에는, 직접 메서드를 구현해야하는 복잡성이 있었다.
그러나 하나의 파라미터(익명클래스)로 다양한 동작을 다룰 수 있다는 강점이 생겼다.

마무리하며

  • 동작 파라미터화를 통해 요구사항의 변화에 유연하게 대응하는 설계를 배웠다.
  • 또한 람다 표현식을 통해서 동작 파라미터화를 간결하게 구현할 수 있다는 것을 배웠다.

이제 위의 상황에서도 웃으면서 대답할 수 있을 것이다.

🧑‍🌾 사과를 ~~~로 선별해주세요!
🧑‍💻 네!

다만 늘 람다 표현식이 좋은 것이 아니라는 것을 알고 넘어가자!

복잡한 로직이나 자주 재 사용되는 동작이라면 클래스로 구현하는게 나을 수 있다.
람다식이 너무 길어지면 오히려 가독성이 떨어질 수 있고, 람다 표현식으로 구현한 동작은 일회성으로 사용되기 때문이다.

개발에서는 정답보다는 선택이 있다는 것을 기억하자!

profile
24년도까지 프로젝트 두개를 마치고 25년에는 개발 팀장을 할 수 있는 실력이 되자!

0개의 댓글