SSISO Community

시소당

자바를 이용한 함수 프로그래밍(Functional programming) (한글)

Closure와 Higher Order Function을 사용하여 모듈식의 자바 코드 작성하기

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

수평출력으로 설정

이 페이지 출력

이 페이지를 이메일로 보내기

이 페이지를 이메일로 보내기


제안 및 의견
피드백

난이도 : 초급

Abhijit Belapurkar, Senior Technical Architect, Infosys Technologies Limited

2007 년 4 월 24 일

대 규모 개발 프로젝트를 수행 중이라면, 모듈식의 코드를 작성할 때의 장점은 잘 알고 있을 것입니다. 잘 구성된, 모듈식의 코드는 작성, 디버깅, 이해, 재사용이 더욱 쉽습니다. 자바 개발자들이 직면한 문제는 함수 프로그래밍 패러다임이 Haskell, Scheme, Erlang, Lisp 같은 특화된 언어를 통해서만 구현되었다는 점입니다. 이 글에서, Abhijit Belapurkar는 Closure와 Higher Order Function 같은 함수 프로그래밍 구조체를 사용하여 잘 짜인 모듈식의 코드를 자바 언어로 작성하는 방법을 설명합니다. (dW 회원님께서 추천해주신 기사입니다.)

자주 간과되고 있는 자바 언어의 한 가지 측면은 명령 프로그래밍(imperative programming)이라는 점이다. 자바 언어와의 결합으로 매우 광범위하게 퍼졌지만, 명령 프로그래밍은 프로그래밍 스타일의 유일한 선택도 아니며, 가장 효과적인 스타일도 아니다. 이 글에서, 다른 프로그래밍 방법들을 자바 개발 요소들을 자바 개발 프랙티스와 결합할 때의 이점을 설명한다. 구체적으로, 함수 프로그래밍(Functional Programming (FP))에 대해 알아보기로 하겠다.

명령 프로그래밍(Imperative programming)은 프로그램 상태의 관점에서 전산(computation)을 기술하는 방법론이다. 이 패러다임으로 작업하는 프로그래머들은 문(statement)을 사용하여 프로그램 상태를 변경한다. 이것이 바로 자바 프로그램이 컴퓨터가 수행할 명령어(또는 문) 시퀀스로 구성된 이유이다. 반면, 함수 프로그래밍(Functional programming)은 명령의 실행 보다는 식의 계산을 강조하는 프로그래밍 스타일이다. 식(Expression)은 함수들을 사용하여 기본 값들을 결합함으로써 형성된다. 인자로 함수를 호출하는 것과 비슷하다.

이 글에서는, 함수 프로그래밍의 기본적인 특징을 설명하고, 특히 자바 개발 프레임웍에 적용할 수 있는 두 가지 엘리먼트를 집중 설명하겠다. 바로 Closure와 Higher Order Function이다. Python, Ruby, Groovy(참고자료) 같은 Agile 개발 언어로 작업해 보았다면, 이러한 엘리먼트를 이미 봤을 것이다. 이 글에서, 이들을 자바 개발 프레임웍에 직접 적용할 때의 이점에 대해 설명하도록 하겠다. 함수 프로그래밍과 이것의 핵심 엘리먼트의의 개념적인 개요를 설명한 다음, 일반적인 프로그래밍 시나리오를 사용하여 Closure와 Higher Order Function을 사용하는 접근 방식이 자바 코드에 가져오는 이점에 대해 설명하겠다.

함수 프로그래밍이란 무엇인가?

"Why Functional Programming Matters" (참고자료) 의 저자 John Hughes는 ‘모듈성(modularity)은 성공적인 프로그래밍의 열쇠이고, 함수 프로그래밍은 매우 향상된 모듈화(modularization)를 사용한다’고 언급하고 있다. 함수 프로그래밍에서, 프로그래머에게는 더 작고, 더 간단하고, 보다 일반적인 모듈들을 개발할 수 있는 프레임웍이 주어지며, 이러한 모듈들은 서로 결합된다. 함수 프로그래밍의 기본적인 특징들은 다음과 같다.

  • Closure와 Higher Order Function 지원
  • Lazy Evaluation 지원
  • 컨트롤 흐름을 위한 메커니즘으로서 회귀(recursion) 사용
  • 참조 투명성(referential transparency)의 강화
  • 부작용이 없음

자바 언어의 Closure와 Higher Order Function의 사용법을 집중적으로 설명하겠지만, 먼저 위에 열거한 특성들에 대해 살펴보도록 하자.

Closure와 Higher Order Function

함수 프로그래밍은 퍼스트 클래스 객체로서 가끔, closures, 또는 functor 객체라고 하는 함수들을 지원한다. 주로, Closure는 함수로서 작동하는 객체이고, 객체처럼 연산될 수 있다. 이와 비슷하게, FP 언어는 higher order functions를 지원한다. Higher Order Function은 인풋 인자로서 또 다른 함수(간접적으로는, 식)을 취할 수 있고, 어떤 경우에는, 아웃풋 인자로서 함수를 리턴하기도 한다. 이러한 두 개의 구조들이 훌륭한 방식으로 프로그램을 모듈화 하고, 이는 FP를 사용할 때의 가장 큰 이점이 된다.

명령 프로그래밍

명령 프로그래밍(imperative programming)이란 이름은 영어 같은 자연어의 명령법(imperative mood)에서 파생한 용어이다. 여기에서, 명령(command)이 선언되고 실행된다. 자바 언어 외에도, C와 C++도 명령 스타일에 순응하는 고급 프로그래밍 언어로서 광범위하게 사용되고 있다.

Lazy evaluation

Higher Order Function과 Functor(또는 Closure)의 개념 말고도, FP는 lazy evaluation의 개념을 사용하고 있다. Lazy evaluation에서, 식은 이것이 변수로 묶이자마자 계산되지 않고, 이밸류에이터(evaluator)가 실행되어 식의 값을 만들어 낸다. 지연된 계산으로 인해 여러분은 잠재적으로 무한 아웃풋을 만들어 낼 수 있는 함수를 작성할 수 있다. 나머지 프로그램이 필요로 하는 것 이상의 값이 계산되지 않기 때문에 무한 계산으로 인해 생긴 메모리 부족 오류에 대해서는 걱정할 필요가 없다. Lazy evaluation의 한 예제는 Fibonacci 넘버의 무한 리스트를 만들어 내는 함수이지만, nth Fibonacci 넘버의 계산은 무한 리스트에서 단 한 개의 아이템을 뺀 것과 같다.

회귀(Recursion)

FP 는 또한 컨트롤 흐름을 위한 메커니즘으로서 회귀(Recursion)를 사용한다. 예를 들어, Lisp는 헤드 엘리먼트와 그 뒤를 따르는 하위 리스트(sub-list)로 정의된 리스트를 사용하는데, 그 개념은 더 작은 하위 리스트에 대해 회귀를 실행하는 표기법이다.

구현 라이브러리에 대해서

필자는 Apache Commons Functor project에 서 제공하는 구현 라이브러리를 사용하여 이 글에 사용되는 예제를 구현했다. Apache Commons Functor 라이브러리는 Closure와 Higher Order Function이 포함된 복잡한 시나리오에서 재사용 될 수 있는 기본 구조들의 방대한 컬렉션들로 구성된다. 비록, Apache Commons Functor 라이브러리를 다운로드 및 사용하여 여기에 나온 예제를 실행해야 하지만, 다른 구현들(Java Generic Libraries, Mango, Generic Algorithms for Java)도 이 글에 소개된 개념들에는 어떤 영향도 주지 않고 사용될 수 있다.

참조 투명성(Referential transparency)

함 수 프로그램들은 일반적으로 참조 투명성(referential transparency)이 있다. 같은 인풋이 제공되는한 함수는 언제나 같은 결과를 리턴한다. 해당 식(expression)의 값은 글로벌 상태에 의존하지 않고 값을 변경할 수 있다. 이것을 통해 여러분은 프로그램 작동에 대해 생각할 수 있다. 식의 의미는 계산의 순서나 다른 식의 부작용(side-effect)이 아닌, 하위 식의 의미에만 의존하기 때문이다. 이것은 정확성을 입증하고, 알고리즘을 단순화 하며, 최적화 할 방법을 찾는데 도움이 된다.

부작용

부 작용은 시스템의 상태를 수정하는 언어 구조이다. FP 언어에는 어떤 할당 문(assignment statement)도 포함하고 있지 않으므로 변수 값이 할당되면 절대로 변하지 않는다. 또한, 호출되는 함수는 결과의 계산이 된다. 다른 결과가 발생할 수 없다. 따라서, FP 언어는 어떤 부작용도 없다.

이러한 기본적인 아웃라인으로도 이 글의 함수 프로그래밍 예제들을 충분히 실행할 수 있다. 자세한 내용은 참고자료 섹션을 참조하라.




위로


자바 언어의 함수 프로그래밍

여 러분은 아마도 당시에는 깨닫지 못했겠지만, 자바 개발을 하면서 Closure와 Higher Order Function을 경험한 적이 있을 것이다. 예를 들어, 많은 자바 개발자들은 실행을 위한 익명의 내부 클래스 안에 자바 코드의 어휘 단위를 인클로즈 할 때 closure를 만난다. 인클로즈 된 자바 코드의 단위가 higher order function에 의해서 온 디맨드(on demand)로 실행된다. 예를 들어, Listing 1의 자바 코드는 java.lang.Runnable 유형의 객체 내에 메소드 호출을 인클로즈 한다.


Listing 1. 위장한 Closure
Runnable worker = new Runnable()
{
public void run()
{
parseData();
}
};

parseData 메소드는 문자 그대로 Runnable 객체 인스턴스인, worker 내에 인클로즈 된다.(따라서 이름이 "Closure"이다.) 이것은 마치 데이터인 것처럼 메소드 사이를 돌아다닐 수 있고, 메시지(run)를 worker 객체로 보내서 언제라도 실행될 수 있다.

추가 예제

객체 지향 세계에서 Closure와 Higher Order Function의 또 다른 사용 예제는 Visitor 패턴이다. 이 패턴을 잘 모르겠다면, 참고자료 섹션을 참조하라. 기본적으로 Visitor 패턴은 Visitor 라고 하는 참여자를 제공하고, 이것의 인스턴스가 복합 객체(또는 데이터 구조)에 의해 수락되고 데이터 구조의 각각의 구성 노드들에 적용된다. Visitor 객체는 로직을 인클로즈 하여, 노드/엘리먼트를 처리하며, 데이터 구조의 accept (visitor) 메소드를 Higher Order Function으로서 사용하여 로직을 적용한다.

완 전히 다른 프로세싱 로직이 약간 다른 Visitor 객체(Closure)를 사용함으로써, 데이터 구조의 엘리먼트에 적용될 수 있다. 마찬가지로, 같은 Closure를 다른 Higher Order Function에 전달하여 데이터 구조를 또 다른 방식으로 처리할 수 있다. (예를 들어, 새로운 Higher Order Function은 구성 엘리먼트들을 반복하기 위해 다른 로직을 실행할 수 있다.)

또 다른 예제는 Java 2 SDK 1.2 버전 이후 일부가 된 java.utils.Collections 클래스에서 제공된다. 이것이 제공하는 유틸리티 메소드들 중 하나는 java.util.List에 포함된 엘리먼트를 소팅하는 메소드이다. 하지만, 콜러는 java.util.Comparator 유형의 객체에서 리스트의 엘리먼트의 순서를 정하기 위해 로직을 캡슐화 하면서, Comparator 객체는 sort 메소드에 대한 두 번째 인자로서 전달된다.

내부적으로, sort 메소드의 레퍼런스 구현은 merge-sort 알고리즘에 기반하고 있다. 이것은 인접 객체에 순서대로 Comparator 객체의 compare 메소드를 호출함으로써 리스트에 있는 엘리먼트를 반복한다. 이 경우, Comparator는 Closure이고, Collections.sort 메소드는 Higher Order Function이다. 이 방식의 장점은 명확하다. 리스트에 포함된 객체의 유형에 따라 다른 Comparator 객체들을 처리할 수 있다. (이 리스트에 있는 두 개의 객체들을 비교하는 방법에 대한 로직은 Comparator 객체에 안전하게 캡슐화 된다.) 마찬가지로 sort 메소드의 또 다른 구현은 완전히 다른 알고리즘(이를 테면, quick-sort)을 사용할 수 있지만, 같은 Comparator 객체를 재사용하면서, 이것을 리스트에 있는 두 개의 엘리먼트들의 결합에 대한 기본 함수로서 적용한다.




위로


Closure 생성하기

일 반적으로 말해서, Closure를 만드는 두 개의 기술이 있고, 이 중 하나는 Closure를 사용할 코드에 의해 똑같이 적용될 수 있다. 일단 Closure를 만들고 나면, 이것을 일관된 방식으로 처리할 수 있고, 또한 메시지를 보내서 인클로징 로직을 실행할 수 있다. 따라서, 이 기술의 선택은 어떤 상황에서는 선호도의 문제이다.

다운로드 공지

이 글에서는 Apache Commons Functor 라이브러리에 기반한 예제들을 사용할 예정입니다. 라이브러리를 아직 다운로드 하지 않았다면, 지금 다운로드 하십시오. 여러분이 Apache 라이브러리가 있는 Javadocs에 액세스 할 수 있다고 가정하고, 개별 라이브러리 클래스들을 설명할 것입니다.

첫 번째 기술인 expression specialization의 경우, Closure에 대한 일반 인터페이스는 인프라스트럭처에 의해 제공되고, 구체적인 Closure는 특수한 인터페이스 구현을 작성함으로써 생성된다. 두 번째 기술인 expression composition의 경우는, 인프라스트럭처는 근본적인 unary/binary/ternary/.../n-ary 연산을 실행하는 구체적인 헬퍼 클래스를 제공한다. (not은 unary 연산자, and / or는 바이너리 연산자). 이 경우, 새로운 Closure는 이러한 기본 구현 블록에서 형성된 임의의 컴포지션으로서 생성된다.

이제부터 각각의 기술을 상세하게 설명하겠다.




위로


Expression specialization

온라인 상점용 애플리케이션을 작성한다고 해보자. 이 상점에서 사용할 수 있는 아이템들은 SETLItem 클래스에 의해 모델링 된다. 각 아이템에는 표시된 가격이 있다. SETLItem 클래스는 getPrice라고 하는 메소드를 제공한다. 이것은 아이템 인스턴스의 가격을 리턴한다.

item1 가격이 item2와 거의 같은지 어떻게 체크할까? 자바 언어에서는 일반적으로 다음과 같은 식을 작성한다.

assert(item1.getPrice() >= item2.getPrice());

이와 같은 식을 바이너리 술어(binary predicate)라고 한다. binary는 이것이 두 개의 인자를 취하기 때문이고, 술어(predicate)는 이것이 두 개의 인자를 사용하여 어떤 것을 수행하고 Boolean 결과를 만들어 내기 때문이다. 하지만, 위 식은 실행 흐름에서 만났을 때에만 실행될 수 있고, 이것의 결과는 특정 인스턴스에 있는 item1item2의 값에 의존한다. 함수 프로그래밍의 관점에서 볼 때, 이 식은 아직 일반적인 로직은 아니다. 다시 말해서, 실행 컨트롤의 현재 위치와 관계 없이, 여러분이 원할 때마다 실행될 수 없다는 것을 의미한다.

바이너리 술어가 작동하도록 하려면, 이것을 객체 안에 인클로즈 해야 한다. BinaryPredicate라고 하는 인터페이스를 특화하여 이를 수행하는데, 이것은 Apache Functor 라이브러리가 제공한다. (Listing 2)


Listing 2. Expression Specialization 방식
package com.infosys.setl.fp;
public class SETLItem
{
private String name;
private String code;
private int price;
private String category;

public int getPrice()
{
return price;
}

public void setPrice(int inPrice)
{
price = inPrice;
}

public String getName()
{
return name;
}

public void setName(String inName)
{
name = inName;
}

public String getCode()
{
return code;
}

public void setCode(String inCode)
{
code = inCode;
}

public String getCategory()
{
return category;
}

public void setCategory(String inCategory)
{
category = inCategory;
}
}


package com.infosys.setl.fp;
import java.util.Comparator;
public class PriceComparator implements Comparator
{
public int compare (Object o1, Object o2)
{
return (((SETLItem)o1).getPrice()-((SETLItem)o2).getPrice());
}
}


package com.infosys.setl.fp;
import org.apache.commons.functor.*;
import org.apache.commons.functor.core.comparator.*;
import java.util.*;
public class TestA
{
public static void main(String[] args)
{
try
{
Comparator pc = new PriceComparator();
BinaryPredicate bp = new IsGreaterThanOrEqual(pc);
SETLItem item1 = new SETLItem();
item1.setPrice(100);
SETLItem item2 = new SETLItem();
item2.setPrice(99);
if (bp.test(item1, item2))
System.out.println("Item1 costs more than Item2!");
else
System.out.println("Item2 costs more than Item1!");

SETLItem item3 = new SETLItem();
item3.setPrice(101);
if (bp.test(item1, item3))
System.out.println("Item1 costs more than Item3!");
else
System.out.println("Item3 costs more than Item1!");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

BinaryPredicate 인터페이스는 Apache Functor 라이브러리에서 제공되는 IsGreaterThanOrEqual 클래스의 형태로 특화되었다. PriceComparator 클래스는, java.util.Comparator 인터페이스를 실행하는데, 인풋으로서 IsGreaterThanOrEqual 클래스로 전달된다. IsGreaterThanOrEqual 클래스는 test 메시지가 보내질 때 자동으로 PriceComparator 클래스의 compare 메소드를 호출한다. compare 메소드는 두 개의 SETLItem 객체들로 전달 될 것이다. 이렇게 되면 이것은 두 아이템의 가격의 차이를 리턴한다. compare 메소드의 양수 응답은 item2와 비슷하게 가격이 나가는 item1으로서 인터프리팅 된다.

언뜻 보기에는, 이것은 매우 기본적인 연산에 많은 작업을 하는 것처럼 보이는데, 그렇다면 이것의 장점은 무엇인가? (자바 비교 식을 작성하는 대신) BinaryPredicate 인터페이스를 특화하면 언제 어디서나, 두 개의 아이템 가격을 비교할 수 있도록 설정할 수 있다. 데이터로서 bp 객체를 전달하고, 여기에 원하는 시간 언제나 시행할 수 있는 메시지(test)를 보낼 수 있고, 두 개의 인자에 원하는 값 아무거나 사용할 수 있다.




위로


Expression composition

식 구성은 같은 결과를 만들어 내는 약간 다른 기술이다. 현재 디스카운트와 판매 세율도 고려하여 특정 SETLItem의 원가를 계산하는 문제를 생각해 보자. 이러한 문제를 푸는 Functor 기반 방식의 소스 코드는 Listing 3과 같다.


Listing 3. Expression Composition 방식
package com.infosys.setl.fp;
import org.apache.commons.functor.BinaryFunction;
import org.apache.commons.functor.UnaryFunction;
import org.apache.commons.functor.adapter.LeftBoundFunction;
public class Multiply implements BinaryFunction
{
public Object evaluate(Object left, Object right)
{
return new Double(((Double)left).doubleValue() * ((Double)right).doubleValue());
}
}


package com.infosys.setl.fp;
import org.apache.commons.functor.*;
import org.apache.commons.functor.core.composite.*;
import org.apache.commons.functor.adapter.*;
import org.apache.commons.functor.UnaryFunction;
public class TestB
{
public static void main(String[] args)
{
try
{
double discountRate = 0.1;
double taxRate=0.33;
SETLItem item = new SETLItem();
item.setPrice(100);
UnaryFunction calcDiscountedPrice =
new RightBoundFunction(new Multiply(), new Double(1-discountRate));
UnaryFunction calcTax =
new RightBoundFunction(new Multiply(), new Double(1+taxRate));
CompositeUnaryFunction calcNetPrice =
new CompositeUnaryFunction(calcTax, calcDiscountedPrice);
Double netPrice = (Double)calcNetPrice.evaluate(new Double(item.getPrice()));
System.out.println("The net price is: " + netPrice);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

BinaryFunction와 비슷한 BinaryPredicate은 Apache Functor 라이브러리에서 제공된 일반적인 Functor 인터페이스이다. BinaryFunction 인터페이스는 두 개의 인자를 취하고, Object 값을 리턴한다. 이와 비슷하게, UnaryFunction은 하나의 Object 인자를 취하고 Object 값을 리턴하는 Functor 인터페이스이다.

RightBoundFunction은 Apache 라이브러리에서 제공되는 어댑터 클래스로서, 상수 우측 인자를 사용하여 BinaryFunctionUnaryFunction 인터페이스에 맞게 변경한다. 이것은 하나의 인자를 가진 해당 메시지를 받을 때(evaluate), 내부적으로 evaluate 메시지를 두 개의 인자로 바꾸고 있는 BinaryFunction으로 보낸다. 왼쪽에 있는 것은 여기로 보내졌던 하나의 인자가 되고, 오른쪽 것은 이것에 대해 알고 있는 상수가 된다. 여러분도 생각했겠지만, RightBoundFunction이라는 이름은 상수 값이 두 번째(오른쪽) 인자로서 전달되었다는 사실에서 나온 것이다. (Apache 라이브러리는 상수가 첫 번째, 또는 왼쪽 인자로서 전달되는 LeftBoundFunction도 제공한다.)

Double을 곱하는 특화된 BinaryFunction

Listing 3은 두 개의 Double을 인풋으로서 취하고 이전 두 개를 함께 곱하여 나온 새로운 Double을 리턴하는 이른바 Multiply라는 특화된 BinaryFunction을 보여주고 있다.

새로운 RightBoundFunction(1 - discountRate)을 이것의 상수 두 번째 인자로서 사용함으로써 바이너리 Multiply 함수를 unary 인터페이스에 맞게 바꾸는 calcDiscountedRate에서 인스턴스화 된다.

결과적으로, evaluate이라고 하는 메시지를 하나의 Double 인자를 가진 calcDiscountRate으로 보낼 수 있다. 내부적으로, 인풋 Double 인자는 calcDiscountRate 객체 내에 보유된 상수 값에 곱해진다.

비슷하게, 새로운 RightBoundFunction(1 + taxRate)을 이것의 상수 두 번째 인자로 사용함으로써 바이너리 Multiply 함수를 unary 인터페이스로 바꾸는 calcTaxRate에서 인스턴스화 된다. 결과적으로 여러분은 evaluate이라는 메시지를 하나의 Double 인자를 가진 calcTaxRate으로 보낼 수 있다. 내부적으로, 인풋 Double 인자가 calcTaxRate 객체 안에 보유된 상수 값에 곱해진다.

하나의 매개변수를 지닌 함수들을 컴포지션(composition) 함으로서, 다중 매개변수들로 이루어진 하나의 함수를 작성 기술을 커링(currying)이라고 한다.

컴포지션 기법은 마지막 부분에서 제 역할을 한다. 객체의 원가를 계산하는 알고리즘은 먼저 할인된 가격(calcDiscountRate functor)를 계산하고, 여기에 (calcSalesTax functor를 사용함) 영업 세금을 포함시켜 원가를 계산한다. 다시 말해서, 내부에서 첫 번째 Functor를 호출하고 그 계산의 아웃풋을 인풋으로서 두 번째 Functor의 계산으로 보내는 함수를 구성해야 한다. Apache 라이브러리는 CompositeUnaryFunction이라는 빌트인 Functor를 제공한다.

Listing 3에서, CompositeUnaryFunctioncalcDiscountRatecalcSalesTax의 컴포지션으로서 calcNetPrice 변수로 인스턴스화 된다. 전과 같이, 이 객체를 다른 객체들로 전달할 수 있다. 이 객체는 evaluate 메시지를 해당 아이템을 인자로서 포함하고 있는 곳으로 보냄으로써 그 아이템의 원가를 계산하도록 요청한 객체이다.

unary 컴포지션과 바이너리 컴포지션

Listing 3에서, unary 컴포지션(unary composition)의 예를 보았는데, 여기에서 한 unary Functor의 결과는 또 다른 것에 대한 인풋이었다. 또 다른 유형의 컴포지션을 바이너리 컴포지션(binary composition)이라고 하는데, 여기에서는 두 개의 unary Functor들의 결과가 evaluate메시지의 일부로서, 바이너리 functor에 대한 매개변수로 전달된다.

Listing 4는 바이너리 컴포지션의 특징과 스타일 예제를 보여주고 있다. 여러분 스토어가 줄 수 있는 최대 디스카운트의 최대 한계가 정해졌다고 가정해 보자. 따라서, 여러분은 cap 값으로 calcDiscount functor의 계산 결과로 얻은 디스카운트 값을 비교하고 디스카운트 된 값을 계산할 때 이 두 개 중에서 최소를 취해야 한다. 디스카운트 된 가격은 정가에서 실제 디스카운트를 빼면서 계산된다.


Listing 4. 바이너리 컴포지션
package com.infosys.setl.fp;
import org.apache.commons.functor.BinaryFunction;
public class Subtract implements BinaryFunction
{
public Object evaluate(Object left, Object right)
{
return new Double(((Double)left).doubleValue() - ((Double)right).doubleValue());
}
}


package com.infosys.setl.fp;
import org.apache.commons.functor.BinaryFunction;
import org.apache.commons.functor.UnaryFunction;
public class BinaryFunctionUnaryFunction implements UnaryFunction
{
private BinaryFunction function;

public BinaryFunctionUnaryFunction(BinaryFunction f)
{
function=f;
}

public Object evaluate(Object obj)
{
return function.evaluate(obj,obj);
}
}



package com.infosys.setl.fp;
import org.apache.commons.functor.*;
import org.apache.commons.functor.core.composite.*;
import org.apache.commons.functor.adapter.*;
import org.apache.commons.functor.UnaryFunction;
import org.apache.commons.functor.core.Constant;
import org.apache.commons.functor.core.comparator.Min;
public class TestC
{
public static void main(String[] args)
{
double discountRate = 0.1;
double taxRate=0.33;
double maxDiscount = 30;
SETLItem item = new SETLItem();
item.setPrice(350);
UnaryFunction calcDiscount =
new RightBoundFunction(new Multiply(), new Double(discountRate));
Constant cap = new Constant(new Double(maxDiscount));
BinaryFunction calcActualDiscount =
new UnaryCompositeBinaryFunction (new Min(), calcDiscount, cap);
BinaryFunctionUnaryFunction calcActualDiscountAsUnary =
new BinaryFunctionUnaryFunction(calcActualDiscount);
BinaryFunction calcDiscountedPrice =
new UnaryCompositeBinaryFunction (new Subtract(), new Identity(),
calcActualDiscountAsUnary);
BinaryFunctionUnaryFunction calcDiscountedPriceAsUnary =
new BinaryFunctionUnaryFunction(calcDiscountedPrice);
UnaryFunction calcTax =
new RightBoundFunction(new Multiply(), new Double(1+taxRate));
CompositeUnaryFunction calcNetPrice =
new CompositeUnaryFunction(calcTax, calcDiscountedPriceAsUnary);
Double netPrice = (Double)calcNetPrice.evaluate(new Double(item.getPrice()));
System.out.println("The net price is: " + netPrice);
}
}

Apache Functors 라이브러리에서 사용했던 세 가지 표준 Functor들을 보면서, 이 코드의 압축을 풀고 파악해야 한다.

  • UnaryCompositeBinaryFunction functor는 인풋으로서 바이너리 함수와 두 개의 unary 함수를 취한다. 후자의 두 개가 먼저 계산되고, 이들의 아웃풋이 바이너리 함수에 대한 인풋으로서 전달된다. Listing 4에서 바이너리 컴포지션에 대해 이 Functor를 두 번 사용했다.

  • Constant functor의 계산은 언제나 상수 값을 리턴한다. 나중에 여기로 보내 질 어떤 계산 메시지를 사용하여 전달된 인자와 무관하다. Listing 4에서, 변수 capConstant 유형의 변수이고, 언제나 최대 디스카운트 금액을 리턴한다.

  • Identity functor는 아웃풋으로서 evaluate 메시지에 대한 인풋 인자로서 전달된 같은 객체를 리턴한다. Listing 4는 calcDiscountedPrice의 생성 시 unary Functor들 중 하나로 생성 및 전달된 Identity functor의 인스턴스를 보여준다. 또한, Listing 4에서, evaluate 메시지에는 인자로서 정찰 가격을 포함하고 있기 때문에, Identity functor는 정찰 가격을 아웃풋으로서 리턴한다.

첫 번째 바이너리 컴포지션은 calcActualDiscount(정찰 가격에 디스카운트를 직접 적용함)와 cap을 계산하는 calcDiscount 변수를 UnaryCompositeBinaryFunction을 사용하여 설정할 때 보인다. 이러한 두 개의 unary Functor들의 계산 결과는 Min이라고 하는 빌트인 바이너리 Functor로 전달되는데, 이것은 두 개를 비교하고 두 개의 값들 중 더 작은 것을 리턴한다.

이 예제는 BinaryFunctionUnaryFunction 커스텀 클래스를 보여준다. 이 클래스는 바이너리 Functor를 고쳐서 unary Functor의 것과 비슷한 인터페이스를 갖도록 한다. 다시 말해서, 이 클래스가 하나의 인자를 가진 evaluate 메시지를 받으면, 이것은 내부적으로 (인클로즈드 바이너리 함수에) evaluate 메시지를 보내는데, 이것의 두 개의 인자들은 인풋으로서 받았던 것과 같은 객체이다. calcActualDiscount는 바이너리 함수이기 때문에, 여러분은 이것을 BinaryFunctionUnaryFunction 유형의 calcActualDiscountAsUnary 인스턴스를 통해 unary Functor 인터페이스 내부에서 이를 래핑한다. unary Functor로서 calcActualDiscount를 래핑하는 이유는 곧 명확해 진다.

두 번째 바이너리 컴포지션은 calcDiscountedPrice 변수가 evaluation 메시지를 새로운 Identity 인스턴스와 calcActualDiscountAsUnary 객체로 보내는 (이 때, 정찰 가격은 두 개의 메시지에 대한 인풋 인자가 된다.) UnaryCompositeBinaryFunction으로 설정될 때 발생한다.

이러한 두 개의 계산 결과(각각 정찰 가격과 실제 할인 가격이 됨)는 Subtract라고 하는 커스텀 바이너리 Functor로 전달된다. 후자 객체가 evaluate 메시지로 전달되자마자, 이것은 이 두 인자들의 차이를 계산하고 리턴한다. (해당 아이템의 디스카운트 가격이 된다.) 또한, 커스텀 BinaryFunctionUnaryFunction을 사용하여 calcDiscountedPriceAsUnary라고 하는 unary Functor 객체로서 바이너리 Functor를 래핑한다.

이전 경우와 마찬가지로, 이 코드는 두 개의 unary Functor들인 calcTax(Listing 3)와 calcDiscountedPriceAsUnary(이전 단락)을 사용하여 CompositeUnaryFunction을 만듦으로써 unary 컴포지션으로 끝난다. calcNetPrice는 하나의 인자(해당 아이템의 정찰가)를 가진 evaluate 메시지를 수락하고, 내부적으로 인자를 사용하여 calcDiscountedPriceAsUnary functor를 계산한 다음, 이전 계산의 결과를 매개변수로서 전달하면서 calcTax functor를 계산한다.




위로


Closure를 사용하여 비즈니스 규칙 실행하기

Apache Library는 광범위한 빌트인 unary 및 바이너리 Functor들을 제공하여 비즈니스 규칙들을 객체로서 쉽게 작성할 수 있도록 한다. 이전 섹션에서 언급했던 것처럼, 간단한 예제를 사용하여 익숙한 문제에 대한 함수 프로그래밍 접근 방식을 설명하도록 하겠다.

특정 아이템이 카테고리와 이 아이템의 리스트 가격에 따라 디스카운트의 가능/불가능 하다고 가정해 보자. 특히, 리스트 가격이 $100 이상인 Category "A" 아이템과 리스트 가격이 $200 이상인 Category "B"만 디스카운트 가능하다. Listing 5의 코드는 isEligibleForDiscount라고 하는 비즈니스 규칙 객체(UnaryPredicate)를 보여주고 있다. 인자로서 item 객체와 함께 evaluate 메시지로 보내지고, 디스카운트가 적용될지 여부를 나타내는 Boolean을 리턴한다.


Listing 5. 비즈니스 규칙 객체
package com.infosys.setl.fp;
import org.apache.commons.functor.BinaryPredicate;
import org.apache.commons.functor.UnaryPredicate;
public class BinaryPredicateUnaryPredicate implements UnaryPredicate
{
private BinaryPredicate bp;

public BinaryPredicateUnaryPredicate(BinaryPredicate prd)
{
bp=prd;
}

public boolean test(Object obj)
{
return bp.test(obj,obj);
}
}


package com.infosys.setl.fp;
import org.apache.commons.functor.*;
import org.apache.commons.functor.core.composite.*;
import org.apache.commons.functor.adapter.*;
import org.apache.commons.functor.UnaryFunction;
import org.apache.commons.functor.core.Constant;
import org.apache.commons.functor.core.IsEqual;
import org.apache.commons.functor.core.comparator.IsGreaterThanOrEqual;
import org.apache.commons.functor.core.comparator.Min;
import org.apache.commons.functor.core.Identity;
public class TestD
{
public static void main(String[] args)
{
SETLItem item1 = new SETLItem();
item1.setPrice(350);
item1.setCategory("A");

SETLItem item2 = new SETLItem();
item2.setPrice(50);
item2.setCategory("A");

SETLItem item3 = new SETLItem();
item3.setPrice(200);
item3.setCategory("B");

UnaryFunction getItemCat =
new UnaryFunction()
{
public Object evaluate (Object obj)
{
return ((SETLItem)obj).getCategory();
}
};

UnaryFunction getItemPrice =
new UnaryFunction()
{
public Object evaluate (Object obj)
{
return new Double(((SETLItem)obj).getPrice());
}
};

Constant catA = new Constant("A");
Constant catB = new Constant("B");
Constant usd100 = new Constant(new Double(100));
Constant usd200 = new Constant(new Double(200));

BinaryPredicateUnaryPredicate belongsToCatA = new BinaryPredicateUnaryPredicate
(new UnaryCompositeBinaryPredicate(new IsEqual(), getItemCat, catA));
BinaryPredicateUnaryPredicate belongsToCatB = new BinaryPredicateUnaryPredicate
(new UnaryCompositeBinaryPredicate(new IsEqual(), getItemCat, catB));

BinaryPredicateUnaryPredicate moreThanUSD100 = new BinaryPredicateUnaryPredicate
(new UnaryCompositeBinaryPredicate(new IsGreaterThanOrEqual(), getItemPrice, usd100));
BinaryPredicateUnaryPredicate moreThanUSD200 = new BinaryPredicateUnaryPredicate
(new UnaryCompositeBinaryPredicate(new IsGreaterThanOrEqual(), getItemPrice, usd200));

UnaryOr isEligibleForDiscount = new UnaryOr(new UnaryAnd(belongsToCatA, moreThanUSD100),
new UnaryAnd(belongsToCatB, moreThanUSD200));

if (isEligibleForDiscount.test(item1))
System.out.println("Item #1 is eligible for discount!");
else
System.out.println("Item #1 is not eligible for discount!");

if (isEligibleForDiscount.test(item2))
System.out.println("Item #2 is eligible for discount!");
else
System.out.println("Item #2 is not eligible for discount!");

if (isEligibleForDiscount.test(item3))
System.out.println("Item #3 is eligible for discount!");
else
System.out.println("Item #3 is not eligible for discount!");
}
}

ComparableComparator 사용하기

Listing 5에서 가장 눈에 띠는 것은 isEqual (해당 아이템의 카테고리가 "A"인지 아니면 "B"인지를 검사함)과 isGreaterThanOrEqual(해 당 아이템의 리스트 가격이 지정된 가격, Category "A" 아이템의 경우는 100, Category "B" 아이템의 경우는 200과 같은지 또는 그 이상인지를 검사함)이라고 하는 빌트인 바이너리 술어 Functor를 사용하고 있다는 점이다.

Listing 2를 회상해 보면 알겠지만, 여러분은 이전에 가격 비교에 isGreaterThanOrEqual functor를 사용하기 위해서 PriceComparator 객체(비교 로직을 캡슐화 함)를 전달했어야 했다. Listing 5에서는 Comparator 객체를 명확하게 처리하지 않았다. 어떻게 그럴 수 있을까? 트릭은 isGreaterThanOrEqual functor(그리고 IsEqual functor까지도) 여러분이 지정하지 않았다면 기본 ComparableComparator를 사용한다. 이 기본 Comparator는 비교되는 두 개의 객체들이 java.lang.Comparable을 실행하고 첫 번째 인자에 대해 compareTo 메소드를 호출하고(Comparable로서 할당한 후에), 이 메소드에 대한 매개변수로서 두 번째 인자를 처리한다.

비교하는 일을 객체에 위임함으로써, 기본 Comparator는 그 자체로 String 비교(item 카테고리에 수행했던 것과 같음)와 Double 비교 (item 가격에 했던 것과 같음)용으로 남아있게 된다. StringDoubleComparable 인터페이스를 실행하는 기본 자바 유형이다.

바이너리 술어를 unary로 바꾸기

두 번째로 주목해야 할 것은 BinaryPredicateUnaryPredicate라고 하는 새로운 Functor를 사용했다는 점이다. 이 Functor(Listing 4BinaryFunctionUnaryFunction functor와 비슷)는 바이너리 술어 인터페이스를 unary로 바꾼다. BinaryPredicateUnaryPredicate functor는 하나의 인자를 가진 unary 술어로 생각할 수 있다. 이것은 내부에서 래핑된 바이너리 술어를 같은 인자의 두 개의 카피들을 사용하여 계산한다.

isEligibleForDiscount 객체는 하나의 완전한 비즈니스 규칙을 캡슐화 한다. 여러분도 알다시피, 이것이 구현되는 방식은 약간 더 복잡한 블록들을 구성하는 기본 블록들을 함께 넣고, 그들을 한데로 묶어 보다 복잡한 블록들을 형성하는 식이다. 이렇게 하면 일종의 "시각적인" 규칙 빌더가 된다. 최종 규칙 객체는 즉시에 구현될 수 있는 복잡한 식이 될 수 있고 기반 비즈니스 규칙을 계산하기 위해 처리된다.




위로


컬렉션에 대한 연산

GoF Iterator 패턴(참고자료) 는 기반의 표현을 노출하지 않고 집합 객체의 엘리먼트에 액세스 하는 방식을 제공한다. 이러한 접근 방식의 개념은 반복성 자체가 데이터 구조에서 분리된다는 것이다. (다시 말해서, 컬렉션의 일부가 아니라는 의미이다.) 접근 방식 자체는 컬렉션 내의 특정 위치를 나타내는 객체를 사용하는 것에 기반하고 있고, 컬렉션의 모든 엘리먼트를 이동하는 (컬렉션 내의 논리적 위치를 늘리는 것) 루프 조건을 동반한다. 루프 바디의 나머지 명령들은 현재 Iterator 객체 위치에 있는 컬렉션의 특정 아이템을 검사하고 연산을 수행할 수 있다. 이 경우, 여러분은 반복에 대하 적은 제어권을 갖는다. (예를 들어, next가 얼마나 많이 호출되는가의 관점에서, next 엘리먼트에 액세스를 시도할 때 마다 out-of-range 에러를 우선 검사해야 한다.) 게다가, Iterator는 기반 데이터 구조의 "멤버"에 액세스 하는 모든 사람에게 같은 공용 인터페이스를 사용해야 하고, 이는 매우 비효율적인 액세스를 만든다. 이러한 종류의 Iterator를 "External Iterator"라고 한다.

FP는 이러한 문제에 대해 매우 다른 방식을 취한다. 컬렉션 클래스는 매개변수로서 Functor를 취하고, 내부에서 이를 모든 컬렉션 멤버에게 적용하는 Higher Order Function을 갖고 있다. 이 경우, Iterator는 데이터 구조의 구현을 공유하기 대문에, 반복에 대해서 완벽한 컨트롤을 갖는다. 게다가, 이것은 데이터 구조 멤버들에게 직접적인 액세스를 갖고 있기 때문에 반복은 빠르다. 이러한 종류의 Iterator를 internal Iterator라고 한다.

Apache Functor 라이브러리는 C++ Standard Template Library에 기반하고 있는 내부 Iterator의 변이를 제공한다. Algorithms라고 하는 utility classforeach 메소드를 제공한다. foreach 메소드는 Iterator 객체와 unary Procedure를 인풋으로서 수락하고 Iterator를 트래버스 하는 동안 만나게 되는 각 엘리먼트에 대해 프로시저를 한번 실행한다. (엘리먼트 자체는 프로시저에 대한 싱글 매개변수로서 전달된다.)

내부 Iterator 사용하기

간단한 예제를 통해 외부와 내부 Iterator의 차이를 알아보자. SETLItem 객체 리스트가 있고 $200 이상의 가격을 가진 리스트 상의 아이템에 대해서만 리스트 가격을 추가하도록 등급을 매기도록 요청을 받은 경우를 생각해 보자. 샘플 코드는 Listing 6에 나타나 있다.


Listing 6. 내/외부 Iterator 사용하기
 package com.infosys.setl.fp;
import java.util.*;
import org.apache.commons.functor.Algorithms;
import org.apache.commons.functor.UnaryFunction;
import org.apache.commons.functor.UnaryProcedure;
import org.apache.commons.functor.core.Constant;
import org.apache.commons.functor.core.collection.FilteredIterator;
import org.apache.commons.functor.core.comparator.IsGreaterThanOrEqual;
import org.apache.commons.functor.core.composite.UnaryCompositeBinaryPredicate;
public class TestE
{
public static void main(String[] args)
{
Vector items = new Vector();
for (int i=0; i<10; i++)
{
SETLItem item = new SETLItem();
if (i%2==0)
item.setPrice(101);
else
item.setPrice(i);
items.add(item);
}
TestE t = new TestE();
System.out.println("The sum calculated using External Iterator is: " +
t.calcPriceExternalIterator(items));
System.out.println("The sum calculated using Internal Iterator is: " +
t.calcPriceInternalIterator(items));
}

public int calcPriceExternalIterator(List items)
{
int runningSum = 0;
Iterator i = items.iterator();
while (i.hasNext())
{
int itemPrice = ((SETLItem)i.next()).getPrice();
if (itemPrice >= 100)
runningSum += itemPrice;
}
return runningSum;
}

public int calcPriceInternalIterator(List items)
{
Iterator i = items.iterator();
UnaryFunction getItemPrice =
new UnaryFunction()
{
public Object evaluate (Object obj)
{
return new Double(((SETLItem)obj).getPrice());
}
};
Constant usd100 = new Constant(new Double(100));
BinaryPredicateUnaryPredicate moreThanUSD100 = new BinaryPredicateUnaryPredicate
(new UnaryCompositeBinaryPredicate(new IsGreaterThanOrEqual(), getItemPrice, usd100));
FilteredIterator fi = new FilteredIterator(i, moreThanUSD100);
Summer addPrice = new Summer();
Algorithms.foreach(fi, addPrice);
return addPrice.getSum();
}

static class Summer implements UnaryProcedure
{
private int sum=0;
public void run(Object obj)
{
sum += ((SETLItem)obj).getPrice();
}
public int getSum()
{
return sum;
}
}
}

main() 메소드에서, 여러분은 10개의 아이템 리스트를 설정하고, 짝수 엘리먼트의 가격은 $101이다. (실제 애플리케이션에서 Vector 대신 JDBC 호출의 결과로서 얻어진 ResultSet로 실행한다.)

여러분은 두 개의 다른 메소드를 사용하여 리스트에 대해 원하는 연산을 수행한다. calcPriceExternalIteratorcalcPriceInternalIterator이다. 이름에서 시사하듯, 전자는 ExternalIterator에 기반하고 있고, 후자는 InternalIterator에 기반하고 있다. 이 글에서는 후자 메소드에 대해서 알아보기로 하고, 전자는 자바 개발자에게는 익숙할 것이다. InternalIterator 메소드는 Apache Functor 라이브러리에서 제공되는 두 개의 구조를 사용한다.

첫 번째 구조는 FilteredIterator라고 한다. 이것은 Iterator와 인풋으로서 unary Predicate을 취하고, Iterator와 재미있는 프로퍼티를 리턴한다. 이 프로퍼티에 따르면 Iterator를 트래버스 하는 동안 여러분이 만나게 되는 모든 엘리먼트가 Predicate에 집대성 된 조건을 만족하고 있다. (FilteredIterator의 인스턴스에 의해 리턴된 Iterator는 FilteredIterator의 두 번째 인스턴스에 대한 인풋으로서 전달되어 다양한 기준에 기반하여 엘리먼트들을 뽑아내는 필터 체인을 설정한다.) 이 경우, 여러분은 unary Predicate "is Price greater than or equal to $100" 규칙에 부합하는 아이템에만 관심이 있다. 이 규칙은 moreThanUSD100이라고 하는 BinaryPredicateUnaryPredicate에 명시되어 있는데, 이것은 Listing 5에서 가장 처음 본 것이다.

Apache Functor 라이브러리에서 제공되는 두 번째 구조는 Algorithms라고 하는 유틸리티 클래스이다. 이 예제에서, Summer라고 하는 unary 프로시저는 SETLItem 인스턴스의 리스트 가격을 얻고 이것을 (로컬) 실행 total 변수에 추가한다. 이것은 내부 Iterator 구현의 고전적인 구현이라 할 수 있다.




위로


Functor를 사용한 조작 설정

Functor와 Higher Order Function을 사용하여 모듈식 코드를 작성하는 부분에 대한 글을 많이 썼다. 필자는 마지막 예제로 Functor를 사용하여 실행될 수 있는 조작 연산을 설정하는 부분을 설명하겠다.

일반적으로, 어떤 한 세트의 멤버쉽을 나타내는 두 가지 방식이 있다. 첫 번째는 그 세트의 모든 엘리먼트들을 나열하는 것이다. 이는 자바 프로그래머인 여러분이 전통적으로 사용해온 방식이다. java.util.Set 인터페이스는 add(Object)라고 하는 메소드를 제공하여 인자에서 전달된 객체를 기반 컬렉션에 추가한다.

한 세트의 엘리먼트가 일부 공통 프로퍼티를 공유하지만, 그 세트에 있는 엘리먼트를 고유하게 특화하는 프로퍼티를 기술함으로써 세트의 멤버쉽을 기술하는 것이 더 효과적이다. 세트 멤버들의 수가 너무 커서 메모리에 세트 구현을 유지할 수 없을 경우 후자의 솔루션이 더 알맞다.

이 같은 경우, unary Predicate은 세트를 기술하는데 사용될 수 있다. unary Predicate은 술어가 true로 계산되도록 하는 모든 값들(객체들)의 세트를 정의한다. 사실, 모든 세트 연산은 다른 술어 컴포지션 유형의 관점에서 정의될 수 있다. (Listing 7)


Listing 7. Functor를 사용한 세트 연산
package com.infosys.setl.fp;
import org.apache.commons.functor.UnaryPredicate;
import org.apache.commons.functor.core.composite.UnaryAnd;
import org.apache.commons.functor.core.composite.UnaryNot;
import org.apache.commons.functor.core.composite.UnaryOr;
public class SetOps
{
public static UnaryPredicate union(UnaryPredicate up1, UnaryPredicate up2)
{
return new UnaryOr(up1, up2);
}

public static UnaryPredicate intersection(UnaryPredicate up1, UnaryPredicate up2)
{
return new UnaryAnd(up1, up2);
}

public static UnaryPredicate difference(UnaryPredicate up1, UnaryPredicate up2)
{
return new UnaryAnd(up1, new UnaryNot(up2));
}

public static UnaryPredicate symmetricDifference(UnaryPredicate up1,
UnaryPredicate up2)
{
return difference(union(up1, up2), intersection(up1, up2));
}
}

unary Predicate의 관점에서 unionintersection 연산의 정의는 명확하다. 두 세트들을 나타내는 두 개의 unary Predicate들 중 적어도 하나가 true (logical Or)가 될 경우, 객체는 두 세트의 결합에 속한다. unary Predicate 모두가 true (logical And)가 된다면, 두 세트들의 교집합에 속한다.

이 두 세트들간 차이점은 두 번째가 아닌 첫 번째 세트에 속한 엘리먼트로서 정의된다. 이 정의에 입각하여, 정적 메소드 difference 역시 이해하기 쉽다.

마지막으로, 두 세트들간 차이점은 두 세트들 중 하나에만 속한 모든 엘리먼트의 세트로서 정의된다. 이것은 두 세트들의 결합을 취해서 두 세트들의 교집합에 속한 엘리먼트를 제거한다. 이것은 원래 세트에(unary Predicate) 각각 unionintersection 연산을 적용함으로써 얻어진 두 세트들에 적용된다. 후자 정의는 첫 번째 세 개의 메소드가 네 번째 메소드 내의 구현 블록으로서 사용되는 방법을 명확히 한다.




위로


결론

소셜 북마크

mar.gar.in mar.gar.in
digg Digg
del.icio.us del.icio.us
Slashdot Slashdot

모 듈성은 생산적이고 성공적인 프로그래밍의 열쇠이다. 자바 개발자들의 문제는 모듈화 프로그래밍이 해당 문제들을 부분들로 쪼개는 것 이상을 요한다는 점이다. 또한 작은 범위의 솔루션들을 효과적인 전체로 만들어야 한다. 이러한 유형의 개발은 함수 프로그래밍 패러다임에서 상속된 것이기 때문에 자바 플랫폼에서 모듈식의 코드를 개발할 때 함수 프로그래밍 기술을 사용하는 것이 일반적이다.

이 글에서, 자바 개발 프랙티스에 통합될 수 있는 두 개의 함수 프로그래밍 기술을 설명했다. Closure와 Higher Order Function은 대부분의 자바 개발자들에게는 완전히 생소한 프로그래밍 개념도 아니고, 이들을 효과적으로 결합하여 많은 모듈식의 솔루션들을 만들 수 있다.

Closure와 Higher Order Function을 자바 코드에 결합하고, 함수 프로그래밍의 장점을 이해하는데 이 글이 도움이 되었기 바란다. 참고자료 섹션에서 더욱 자세한 내용을 참조하기 바란다.

기사의 원문보기



참고자료



필자소개


Abhijit Belapurkar는 Indian Institute of Technology (IIT)(인도)에서 컴퓨터 공학 학사 학위를 받았다. 10년 이상을 분산 애플리케이션의 아키텍처와 정보 보안 분야에서 일했으며, 자바 플랫폼에서 n-tier 애플리케이션 구현에도 5년 이상의 경력을 갖고 있다. 현재, Infosys Technologies Limited(인도, 방갈로르)의 J2EE 부분 기술 아키텍트로 일하고 있다.



출처 : http://www.ibm.com/developerworks/kr/library/j-fp.html

795 view

4.0 stars