모던 자바 인 액션 2장

1. 동작 파라미터화

앞장에서 모던 자바가 가지는 매우 큰 변화 중 하나인 메소드 참조에 대해 조금 더 심도있게 정리를 하는 챕터가 바로 이 챕터 2

1장에서 모던 자바가 ‘2급 값’ 인 메소드나 클래스를 ‘1급 값(변수,인스턴스)’ 처럼 간주하여 일종의 값(value) 으로 사용할 수 있다고 하였다.

2급 값을 1급 값으로 간주한다는 것은, 특정 메소드를 호출할 때 caller에서 파라미터로 값(value)뿐만이 아닌 ‘메소드’ 나 ‘클래스’ 자체를 넘겨줄 수 있다는 뜻이 된다.

값(value)가 들어가야 하는 위치인 메소드 아규먼트에 ‘행위’ 자체가 들어가는 것이 바로 ‘동작 파라미터화’ 이다.

사실 핵심은 이게 끝…

  • 레거시 자바
    • caller에서 파라미터로 들어오는 값(value)이, callee 입장에서는 ‘어떤 값’인지는 모름(알 필요가 없고, 알아서도 안됨)
      • 1일수도, 100일수도, 1억일수도…
      • 어떤 이 입력되느냐에 따라 결과가 달라짐. 그저 callee가 갖고 있는 로직이, 파라미터로 들어오는 값에 따라 잘 동작하기만 하면 됨
  • 모던 자바
    • caller에서 파라미터로 들어오는 ‘메소드’가, callee 입장에서는 ‘어떤 동작’인지는 모름(알 필요가 없고, 알아서도 안됨)
      • 사과를 무게로 필터링 하는 것일수도, 색으로 필터링할수도, 아님 파괴하는 동작일수도…
      • 어떤 행위가 입력되느냐에 따라 결과가 달라짐. 그저 callee가 갖고있는 로직이, 파라미터로 들어오는 행위에 따라 잘 동작하기만 하면 됨

C++ 언어(혹은 js)를 해본 경험이 있다면 어디서 많이 본 느낌이다. 콜백함수. 펑션에 파라미터 전달을 ‘참조(레퍼런스)’로 던져, 값이 아닌 동작 자체를 파라미터화 시키는거..

물론 레거시 자바에서도 기능적인 측면에서 이와 ‘비슷한’ 방식으로 구현할 수는 있다. ‘행동’이 아닌 ‘인스턴스’를 파라미터 패싱하여, callee 에서 인스턴스의 특정 메소드를 실행시키는 동작.

  • Runnable 인터페이스를 구현해서 run 메소드를 실행시키는거
뭐 이런거….
https://stackoverflow.com/questions/2186931/java-pass-method-as-parameter

위 코드가 자바7 이전의 레거시 자바에서의 ‘행동’을 파라미터화 시킨 예제이다.

  • 코드를 보면 알겠지만 간접적인 행위임. 여전이 caller는 인스턴스를 생성하여 파라미터 패싱 해 주고 있음.

이 짓까지 하기엔 아무래도 귀찮다. 내가 원하는건 ‘행동’ 하나뿐인데, 이를 인터페이스부터 구현하여, inner class 혹은 implement한 클래스까지 구현해서…. 행위 하나를 1급 값인 변수 혹은 인스턴스화 해야 하기 때문임..

  • 모든 행위를 ‘요구사항이 바뀔 수도 있어!!!’ 하면서 죄다 인스턴스화 하는건 오버스펙임

그래서 모던 자바에서는 2급 값인 이 ‘행위’ 자체를 파라미터로 넘겨줄 수 있도록 기능을 지원해 준다! 예제를 통해 얼마나 편한지 직접 체감해 보자.

2. 레거시 자바(나쁜 예)

사실 모든 내용은 1막이 전부. 하지만 처음 경험하는 입장에서(혹은 커리어가 레거시 자바 외길인생인 경우) 이해가 가지 않을 확률이 매우 높다. 그러니 우린 예제를 통해서 공부를 해 보도록 한다.

1장 포스팅에서 잠깐 사과 필터링에 대한 예제를 본 적이 있을것이다.

기획자로부터 요구조건이 온다

  • 사과를 필터링하고 싶어요
    • 녹색 사과만 받고 싶네요
허허…그렇게 쉬운 요구조건을…

코드 짜서 실 서비스에 배포하였다. 문제없이 잘 작동도 한다

그런데 새로운 요구조건이 온다

  • 사과를 필터링하고 싶어요
    • 생각해보니 빨간 사과도 받고 싶어요.

음…..

개발자는 생각을 한다.

‘세상에는 녹색사과와 빨간사과만 있지만, 0.5픽셀같은 마법의 -빨갛다 만 사과, 누리끼리한 사과, 무지개색 사과- 도 필터링 요청이 오겠지…? 걍 이번 기회에 무슨 색이던 다 받는 코드로 바꾸자’

그래서 색 자체를 파라미터로 받는 코드를 작성한다.

이제 만족하냐?

실 서비스에 리비전 2로 수정배포 완료. 작동도 문제없이 잘 된다.

그런데 새로운 요구조건이 온다

  • 사과를 필터링하고 싶어요
    • 색깔뿐만 아니라 무게로 필터링하고 싶네요

음…………

개발자는 더 이상 생각하기가 싫어졌다. 이건 예상 밖의 요청이었기 때문.

기존에 만든 filterApplesByColor는 색 필터링만 가능한 코드이다. 여기서 무게를 필터링하는 로직을 짜봐야 중복코드를 짤게 분명하다.

딱히 색과 무게를 통합할 무언가가 떠오르지 않고, 또 무게에 대한 다양한 요구조건들이 생길 수도 있으니, 무게를 파라미터로 하는 필터링 로직도 추가한다.

리비전 2랑 비슷한 것 같지만 넘어가자

이젠 더 요구사항 추가가 없겠지 싶어서 배포. 작동도 문제없이 잘 된다.

근데 여기서 생각해 보아야 할게,

중복된 코드가 생겼다.

소프트웨어의 DRY(don’t repeat yourself) 원칙을 어기는 것이다.

이유가 어찌되었건 요구조건이 요상하게 온다고 해도, 그 요상한 요구조건을 코드에 넣는 행동까지는 지양해야 할 것이다.

그래서 개발자는 리비전 3을 따서, 중복을 제거하기 위한 리팩토링을 해 본다.

풀-패키지로 색깔, 무게 속성을 필터링 할 수 있는 하나의 메소드를 만드는 것이다!

한숨이 나온다

뭔가 만들긴 했는데…가독성이 좋지 않다. 몇개월,몇년 후에 내가 짠 코드를 내가 못 알아볼 지경(특히 flag 변수는 지금 봐도 모르겠음.)

3. 동작을 파라미터화 해 보자(좋은 예. 하지만 레거시 자바)

2막에서의 이런저런 요구조건을 반영하기 위해서 코드를 작성하는데, 레거시 자바에서는 이 요구조건을 반영하기 위해서 많은 비용이 든다.

특히 유지보수 측면에서 좋지 못하다.

무게와 색깔을 모두 필터링 할 수 있는 인터페이스를 만들어 본다.

predicate 인터페이스!!!

그리고 이 인터페이스를 구현하는 클래스를 무게,색깔별로 코딩해 주면 된다.

구현

이렇게 준비가 끝났다. callee 메소드인 필터링 메소드는 이제 앞에서 구현한 클래스틑 파라미터로 받아들여, 색깔별로/무게별로 사과를 필터링 할 수 있는 로직을 제작하면 된다.

그럼 이제 저 메소드를 호출할 caller 입장에서는, 사과를 색깔로 필터링할지, 무게로 필터링할지에 따라 파라미터를 다른 값으로 던져주기만 하면 된다.

뭐 이런식으로….

이게 바로 객체지향 언어를 다루면서 배우는 캡슐화(Encapsulation) 의 개념이다.

캡슐화된 ApplePredicate 인터페이스의 정확한 정체를, callee 는 모르는-뭐가 들어오던 상관없는- 그런 구조.

최종(?)적으로 잡은 구조 자체는 흠잡을데 없다.

근데, 솔직히 좀 귀찮다

행위에 대한 정의 하나 파라미터로 넘기자고, 인터페이스 만들고, 그걸 또 구현하는 구현체도 정의해야 하고….. 색깔,무게라는 기준이 아닌 새로운 기준이 생길때마다 ‘클래스’가 늘어난다.(신선도, 유통기한, 수확지역 등등 사과를 필터링하려면 마음껏 필터링 할 수 있는 요소는 널려있다!!)

사과를 필터링하는 메소드 자체는 깔끔해지긴 했지만, 그 ‘깔끔함’을 유지하기 위해 희생되어야 하 비용이 넘나 큰 것.

그래서 (아직 레거시 자바에서는) 익명 클래스를 활용하면 조금 더 간소하게(?) 코드를 짤 수가 있다.

익명 클래스!!!

확실이 아까보다는조금 더 간결해지긴 한 것 같다.

4. 동작 파라미터를 모던 자바로 이걸 표현해 볼까?

코드가 간결해지긴 했지만, 그래도 마음에 들지 않는다.

그래서 모던 자바에서는 메소드를 파라미터로 패싱할 수 있는 기능을 추가함으로써 이런 귀찮은 작업을 매우 간단한 코드로 작성할 수 있게끔 해 주었다.

자, 그럼 코드를 보자

편-안

람다식을 통해 ‘메소드’ 그 자체를 넘겨주니

‘행위를 인스턴스화해서 그 인스턴스를 구현하고, 구현한 인스턴스를 인자로 삼아 파라미터로 넘겨주는’ 귀찮은 작업을 하지 않아도

  • caller에 동작이 드러나면서
  • callee는 caller가 외부에서 뭘 호출하는지 몰라도 정상적으로 필터링한 결과를 내줄 수 있는

완-벽 히 객체지향적인 코드를 작성할 수 있다.

레거시 자바에서는 10줄이 넘는 복잡복잡한 코드를, 모던 자바를 통해서는 단 한줄로 표현할 수 있는 것이다.

5.정리해 보자

  • 행위(2급값)를 인스턴스화(1급값) 강제로 변환하듯이 구현하여 인스턴스를 넘겨 줌 –> 레거시 자바
  • 행위(2급값)그 자체를 파라미터화 하여 넘겨 줌 –> 모던 자바
  • 모던 자바에서 이 기능을 하나 지원해 줌으로써, 개발자는 외부 요구사항에 유연히 대응할 수 있는 코드를 훨씬 더 만들기 쉬워졌다.

Leave a Comment