잠시 독자가 벌목꾼이라고 가정해보자. 숲에서 최고의 도끼를 가지고 있으며, 이는 캠프장에서 가장 생산적인 벌목꾼이 되게 해준다. 그런데 어느 날 어떤 사람이 나타나 새로운 벌목 패러다임의 전기톱의 우수성을 증명한다. 판매하는 사람이 설득력이 있어서 독자는 전기톱을 구입하지만 어떻게 작동하는지를 모른다. 독자는 이를 들어올려서 엄청난 힘으로 휘두르려고 시도하며, 이는 독자의 다른 벌목 패러다임이 작동하는 방식이다. 독자는 이러한 신식의 전기톱은 일시적인 유행에 불과한 것이라고 빠르게 결론 내리고, 도끼로 나무를 자르러 돌아간다. 그러면, 다른 사람이 와서 전기톱을 어떻게 작동하는지 보여준다.
독자는 아마도 전기톱이 아니라 함수형 프로그래밍과 관련하여 이 이야기에 공감할 수 있다. 완전히 새로운 프로그래밍 패러다임의 문제점은 새 언어를 학습하는 것이 아니다. 결국 언어 구문은 세부사항에 불과하다. 까다로운 부분은 다른 방식으로 사고하는 것을 배우는 것이다. 이 점이 바로 필자가 관여하는 것이다— 전기톱 작동자와 함수형 프로그래머.
이제 함수형 사고를 시작한다. 이 시리즈는 함수형 프로그래밍의 주제를 탐색하지만, 함수형 프로그래밍 언어에 대한 것만은 아니다. 필자가 시연하는 대로 "함수형" 방식으로 코드를 쓰면 설계, 교환 조건, 다른 재사용 가능한 빌딩 블록 및 다른 통찰력의 호스트를 다룬다. 필자는 가능한 한 많이 Java(또는 Java 유사 언어)에서 함수형 프로그래밍 개념을 보여주고 다른 언어로 이동하여 Java 언어에서 아직 존재하지 않은 기능을 시연할 것이다. 필자는 곧바로 깊은 쪽으로 뛰어 들어가서 monads처럼 케케묵은 것들에 대해 이야기하지 않을 것이다(비록 이후에 다루기는 하겠지만)(참고자료 참조). 대신에, 점진적으로 문제에 대해 사고하는 새로운 방식을 보여줄 것이다(독자는 일부 지점에서 이미 이를 적용하고 있는 중이다 — 아직 인식하지 않고 있을 뿐이다).
이번 기사와 다음 세 개의 기사는 핵심 개념을 비롯한 함수형 프로그래밍에 관련된 일부 주제의 간략한 방문식으로 살펴본다. 이러한 일부 개념은 필자가 시리즈 전반에 걸쳐서 더 문맥과 미묘한 차이를 쌓으면서 훨씬 더 자세히 다룰 것이다. 이 여행의 출발점으로서, 필자는 두 가지 다른 문제점의 구현방식에 대해 살펴볼 것이다. 하나는 명령형으로 쓰인 구현방식이고 다른 하나는 더 함수형인 방향의 구현방식이다.
다른 프로그래밍 스타일에 대해 논의하려면 비교하기 위한 코드가 있어야 한다. 필자의 첫 번째 예제는 필자의 책인 The Productive Programmer(참고자료 참조) 및 "Test-driven Design, Part 1"과 "Test-driven design, Part 2"(필자의 이전 developerWorks 시리즈인 혁신적인 아키텍처와 창발적 설계)에서 나타나는 코딩 문제의 변형이다. 필자는 이 코드를 적어도 부분적으로 선택했다. 왜냐하면 이러한 두 가지 기사가 코드의 설계를 심도있게 설명하기 때문이다. 이러한 기사에서 극찬한 설계에서 잘못된 점은 없지만, 필자는 여기에서 다른 설계에 대한 근거를 제공할 것이다.
요구사항에 따르면, 1보다 큰 어느 양의 정수나 주어지면, 이를 완전수(perfect), 과잉수(abundant) 또는 deficient(부족수)로 분류해야 한다고 주장한다. 완전수는 그 인수(그 수 자체는 인수로 제외됨)를 합하면 최대 그 수까지 되는 수이다. 이와 유사하게 과잉수의 인수의 합계는 그 수보다 크고, 부족수의 인수의 합계는 그 수보다 적다.
이러한 요구사항에 부합하는 명령 클래스는 목록 1에 나타난다.
목록 1.
NumberClassifier, 문제점에 명령적인 솔루션
public class Classifier6 {
private Set<Integer> _factors;
private int _number;
public Classifier6(int number) {
if (number < 1)
throw new InvalidNumberException(
"Can't classify negative numbers");
_number = number;
_factors = new HashSet<Integer>>();
_factors.add(1);
_factors.add(_number);
}
private boolean isFactor(int factor) {
return _number % factor == 0;
}
public Set<Integer> getFactors() {
return _factors;
}
private void calculateFactors() {
for (int i = 1; i <= sqrt(_number) + 1; i++)
if (isFactor(i))
addFactor(i);
}
private void addFactor(int factor) {
_factors.add(factor);
_factors.add(_number / factor);
}
private int sumOfFactors() {
calculateFactors();
int sum = 0;
for (int i : _factors)
sum += i;
return sum;
}
public boolean isPerfect() {
return sumOfFactors() - _number == _number;
}
public boolean isAbundant() {
return sumOfFactors() - _number > _number;
}
public boolean isDeficient() {
return sumOfFactors() - _number < _number;
}
public static boolean isPerfect(int number) {
return new Classifier6(number).isPerfect();
}
}
|
이 코드에서 다음 몇 가지 사항은 주목할 필요가 있다.
- 이는 광범위한 유닛 테스트를 보유한다(필자가 부분적으로 테스트 구동형 개발의 논의에 대해 썼으므로).
- 해당 클래스는 응집력있는 다양한 메소드를 구성하며, 이는 구성면에서 테스트 구동형 개발을 사용하는 것의 부작용이다.
- 성능 최적화는
calculateFactors()메소드에 임베드되었다. 이 클래스의 본질은 인수를 수집하는 것을 구성하므로, 필자는 이를 합하여 궁극적으로 분류할 수 있다. 인수는 항상 쌍으로 수집할 수 있다. 예를 들어, 문제가 되는 숫자가 16이면, 필자가 인수 2를 취할 때 2 x 8 = 16이므로 8도 취할 수 있다. 필자가 인수를 쌍으로 수집하면 대상 숫자의 제곱근까지 인수에 대해서만 확인해야 하며, 이는 정확하게calculateFactors()메소드가 하는 일이다.
동일한 테스트 구동형 개발 기술을 사용하여 필자는 규정자의 대체 버전을 작성했으며, 이는 다음 목록 2에 나타난다.
목록 2. 약간 더 함수형인 숫자 규정자
public class NumberClassifier {
static public boolean isFactor(int number, int potential_factor) {
return number % potential_factor == 0;
}
static public Set<Integer> factors(int number) {
HashSet<Integer> factors = new HashSet<Integer>();
for (int i = 1; i <= sqrt(number); i++)
if (isFactor(number, i)) {
factors.add(i);
factors.add(number / i);
}
return factors;
}
static public int sum(Set<Integer> factors) {
Iterator it = factors.iterator();
int sum = 0;
while (it.hasNext())
sum += (Integer) it.next();
return sum;
}
static public boolean isPerfect(int number) {
return sum(factors(number)) - number == number;
}
static public boolean isAbundant(int number) {
return sum(factors(number)) - number > number;
}
static public boolean isDeficient(int number) {
return sum(factors(number)) - number < number;
}
}
|
이러한 두 가지 규정자 버전 사이의 차이점은 미묘하지만 중요하다. 주된 차이점은 목록 2에서
공유 상태의 목적이 있는 부재이다. 공유 상태의 제거(또는 적어도 축소)는 함수형 프로그래밍에서 선호하는 추상의
하나이다. 중간 결과 대로 메소드에 걸쳐서 상태를 공유하는 대신에(목록 1의
factors 필드 참조) 필자는 직접 메소드를 호출하여 상태를 제거한다. 설계 관점에서부터
이는 factors() 메소드를 더 길게 만들지만, factors 필드가
메소드로부터 "흘러 나오는 것"을 방지한다. 목록 2 버전이 전체적으로 정적 메소드로
구성될 수 있었음도 주목한다. 메소드 사이에 공유 지식이 존재하지 않으므로, 필자는 범위 지정을 통해 캡슐화에 대한
필요성이 줄어든다. 이러한 모든 메소드는 예상한 대로 입력 매개변수 유형을 제공하는 경우 완벽하게 작업한다. (이는
순수 함수의 예제이며, 다음 기사에서 더 깊이 연구할 개념이다.)
함수형 프로그래밍은 최근에 관심이 폭발하고 있는 전산학의 뻗어 나가는 폭넓은 영역이다. JVM(Scala 및 Clojure 등)에서 새 함수형 언어와 프레임워크(Functional Java 및 Akka 등)가 일반적으로 적은 버그 청구, 더 나은 생산성, 더 우수한 외관, 더 많은 자본 및 기타 등등과 함께 나타난다(참고자료 참조). 필자는 당장 함수형 프로그래밍의 전체 주제를 따지려고 시도하는 것이 아니라 몇 가지 핵심 개념에 초점을 맞추고 이러한 개념에서 유래한 일부 흥미로운 암시를 따라할 것이다.
클래스가 오브젝트 지향 언어에서 주된 추상인 것처럼 함수형 프로그래밍의 핵심은 함수이다. 함수는 처리하기 위한 빌딩 블록을 형성하고 기존의 명령적 언어에 없는 몇 가지 기능으로 채워진다.
고차 함수는 인수로 다른 함수를 취하거나 이를 결과로 리턴할 수 있다. Java 언어에서는 이 구조가 없다. 가장 근접하게 확보할 수 있는 것은 실행하는 데 필요한 메소드의 "홀더"로 클래스(보통 익명 클래스)를 사용하는 것이다. Java는 독립형 함수(또는 메소드)가 없으므로, 이는 함수로부터 리턴될 수 없거나 매개변수로 전달될 수 없다.
이 기능은 적어도 두 가지 이유에서 함수형 언어에 중요하다. 첫 번째로, 고차 함수를 보유하는 것은 언어 부분이 어떻게 함께 맞춰질 것인지에 대해 가정할 수 있다는 것을 의미한다. 예를 들어, 목록을 횡단하고 각 요소에 하나(또는 그 이상)의 고차 함수를 적용하고 일반 메커니즘을 빌드하여 클래스 계층구조에서 메소드의 전체 카테고리를 제거할 수 있다. (필자가 이 구성 예제를 곧 보여줄 것이다.) 두 번째로, 리턴 값으로 함수를 사용하여 고도로 동적인 적응 가능한 시스템을 빌드하는 기회를 작성한다.
고차 함수를 사용하여 솔루션으로 처리할 수 있는 문제점은 함수형 언어에 고유하지 않다. 하지만, 함수적으로 사고할 때에 문제점을 해결하는 방법이 다르다. 보호된 데이터 액세스를 수행하는 메소드의 다음 목록 3(더 큰 규모의 코드베이스에서 발췌됨)의 예제를 고려하자.
목록 3. 잠재적으로 재사용 가능한 코드 템플리트
public void addOrderFrom(ShoppingCart cart, String userName,
Order order) throws Exception {
setupDataInfrastructure();
try {
add(order, userKeyBasedOn(userName));
addLineItemsFrom(cart, order.getOrderKey());
completeTransaction();
} catch (Exception condition) {
rollbackTransaction();
throw condition;
} finally {
cleanUp();
}
}
|
목록 3의 코드는 초기화를 수행하고 일부 작업을 수행하며 모두 성공하면 트랜잭션을 완료하고, 그렇지 않으면 롤백하며 마지막으로 자원을 정리한다. 분명하게 이 코드의 중복된 부분은 재사용될 수 있었으며, 여기에서는 구조를 작성하여 오브젝트 지향 언어에서 일반적으로 그렇게 수행한다. 이 경우에 필자는 Gang of Four Design Patterns의 두 가지를 결합할 것이다(참고자료 참조). 즉, 이는 템플리트 메소드와 명령 패턴이다. 템플리트 메소드 패턴은 필자가 일반적인 중복된 코드를 상속 계층구조 위로 이동해야 함을 암시하며, 하위 클래스로 알고리즘의 세부사항을 지연한다. 명령 설계 패턴은 잘 알려진 실행 시맨틱으로 클래스에서 작동을 캡슐화하는 방법을 제공한다. 목록 4는 이러한 두 가지 패턴을 다음 목록 3의 코드에 적용한 결과를 보여준다.
목록 4. 리팩토링된 순서 코드
public void wrapInTransaction(Command c) throws Exception {
setupDataInfrastructure();
try {
c.execute();
completeTransaction();
} catch (Exception condition) {
rollbackTransaction();
throw condition;
} finally {
cleanUp();
}
}
public void addOrderFrom(final ShoppingCart cart, final String userName,
final Order order) throws Exception {
wrapInTransaction(new Command() {
public void execute() {
add(order, userKeyBasedOn(userName));
addLineItemsFrom(cart, order.getOrderKey());
}
});
}
|
목록 4에서 필자는 코드의 제네릭 부분을 wrapInTransaction() 메소드(독자가 인식할 수 있는
시맨틱— 이는 기본적으로 Spring의 TransactionTemplate의 간단한 버전임)로 추출하여,
Command 오브젝트를 작업의 단위로 전달한다. addOrderFrom() 메소드는
명령 클래스의 익명 내부 클래스 작성의 정의로 축소되어, 두 가지 작업 항목을 랩핑한다.
명령 클래스에서 필자가 필요한 작동을 랩핑하는 것은 순전히 Java 설계의 아티팩트이며, 이는 어떠한 종류의 독립형 작동도 포함하지 않는다. Java에서 모든 작동은 클래스 내부에 상주해야 한다. 심지어 언어 설계자도 이 설계에서 부족분을 빠르게 확인했다 — 지나고 나서 봤을 때, 클래스에 첨부되지 않은 작동이 없을 수 있음을 생각하면 약간 순진한 것이다. JDK 1.1은 익명의 내부 클래스를 추가하여 이러한 부족분을 바로 잡았으며, 이는 구조적이 아니라 순전히 함수형인 불과 몇 가지 메소드로 많은 소규모 클래스를 작성하기 위한 최소한의 신택틱 슈거(syntactic sugar)로 제공된다. 이러한 Java의 측면에 대한 거친 오락성의 재미있는 에세이를 보려면 Steve Yegge의 "Execution in the Kingdom of Nouns"를 확인하자(참고자료 참조).
비록 필자가 정말 원하는 전부가 클래스 내부의 메소드라고 할지라도 Java는
Command 클래스의 인스턴스를 작성하도록 강제 실행한다. 클래스 자체는 이점이 없다. 즉,
이는 필드가 없고, 생성자도 없으며(Java에서 자동 생성된 하나의 생성자 외에는) 상태도 없다. 이는 메소드 내부의 작동에
대한 랩퍼로서의 역할만 온전히 담당한다. 그 대신에 함수형 언어에서 이는 고차 함수를 통해 처리될 것이다.
필자가 Java 언어의 사용을 잠시 중단하려 하는 경우, 필자는 클로저를 사용하여 함수형 프로그래밍 이상향에 시맨틱 상으로 가깝게 접근할 수 있다. 다음 목록 5는 동일한 리팩토링된 예제를 보여주지만, Java가 아니라 Groovy를 사용하는 것이다(참고자료 참조).
목록 5. 명령 클래스 대신에 Groovy 클로저 사용하기
def wrapInTransaction(command) {
setupDataInfrastructure()
try {
command()
completeTransaction()
} catch (Exception ex) {
rollbackTransaction()
throw ex
} finally {
cleanUp()
}
}
def addOrderFrom(cart, userName, order) {
wrapInTransaction {
add order, userKeyBasedOn(userName)
addLineItemsFrom cart, order.getOrderKey()
}
}
|
Groovy에서 구부러진 중괄호 {} 안에 있는 모든 것은 코드 블록이며, 코드 블록은 매개변수로
전달될 수 있어, 고차 함수를 모방한다. 그 이면에서 Groovy는 독자를 위한 명령 설계 패턴을 구현하고 있다. Groovy에서
각 클로저 블록은 실제로 Groovy 클로저 유형의 인스턴스이며, 이는 클로저 인스턴스를 보유하는 변수 이후에 소괄호의
빈 세트를 위치 지정할 때 자동으로 호출되는 call() 메소드를 포함한다. Groovy는
적절한 데이터 구조를 빌드하여 해당하는 신택틱 슈거로 일부 함수형 프로그래밍 유사 작동을 언어 자체로
사용했다. Groovy는 필자가 이후 기사에서 다루는 대로, Java를 넘어서는 다른 함수형 프로그래밍 기능도 포함한다. 필자는
또한 이후의 기사에서 클로저와 고차 함수 사이의 몇 가지 흥미로운 비교를 다시 다룰 것이다.
함수형 언어에서 함수는 최상위 클래스로 고려되며, 이는 다른 언어 구조(변수 등)가 나타날 수 있는 어디서나 함수가 나타날 수 있음을 의미한다. 최상위 함수의 존재를 통해 예상하지 않은 방식으로 함수의 사용이 허용되고 제네릭 연산을 표준 데이터 구조로 상대적으로 적용하는 등(미묘한 차이가 있는 세부사항 사용) 솔루션에 대해 다르게 사고하는 것을 강제 실행한다. 이는 결과적으로 함수형 언어로 사고하는 면에서 기초적인 전환을 드러낸다. 즉, 단계가 아니라 결과에 집중하자.
명령적 프로그래밍 언어에서 필자는 알고리즘에서 각 원자적 단계에 대해 고려해야 한다. 목록 1의 코드는 이를 보여준다. 숫자 분류자를 해결하기 위해 필자는 인자를 수집하는 방법을 정확하게 구별해야 했으며, 이는 필자가 결과적으로 인자를 구별하기 위해 숫자를 통해 루프하도록 특정 코드를 써야만 했음을 의미한다. 하지만 목록을 통해 루프하는 것, 각 요소에서 연산을 수행하는 것은 정말 일반적인 것처럼 보인다. 다음 목록 6에 나타나는 Functional Java 프레임워크를 사용하여 재구현된 숫자 분류 코드를 생각해보자.
목록 6. 함수형 숫자 분류자
public class FNumberClassifier {
public boolean isFactor(int number, int potential_factor) {
return number % potential_factor == 0;
}
public List<Integer> factors(final int number) {
return range(1, number+1).filter(new F<Integer, Boolean>() {
public Boolean f(final Integer i) {
return number % i == 0;
}
});
}
public int sum(List<Integer> factors) {
return factors.foldLeft(fj.function.Integers.add, 0);
}
public boolean isPerfect(int number) {
return sum(factors(number)) - number == number;
}
public boolean isAbundant(int number) {
return sum(factors(number)) - number > number;
}
public boolean isDeficiend(int number) {
return sum(factors(number)) - number < number;
}
}
|
목록 6과 목록 2 사이의 주된 차이점은 두 가지 메소드에 달려있다.
즉, 이는 sum() 및 factors()이다. sum() 메소드는
Functional Java에서 List 클래스의 메소드인 foldLeft()
메소드를 활용한다. 이는 카타모피즘(catamorphism)이라는 목록 조작 개념의 특정 변형이며, 이는 목록 폴딩에 대한
일반화이다. 이 경우에 "fold left"는 다음을 의미한다.
- 초기 값을 취하여 목록의 첫 번째 요소에 대한 연산을 통해 이를 결합한다.
- 결과를 취하여 동일한 연산을 다음 요소에 적용한다.
- 목록이 소진될 때까지 이를 계속 수행한다.
이는 숫자 목록을 합할 때 정확히 수행하는 것임을 주목하자. 0부터 시작하고 첫 번째 요소를 더하여
결과를 취하고 이를 두 번째로 더하고 목록이 소모될 때까지 계속한다. Functional Java는 고차 함수를
공급하고(이 예제에서 Integers.add 열거) 이를 적용하는 것을 처리한다. (물론,
Java는 실제로 고차 함수가 없지만, 특정 데이터 구조와 유형으로 제한하는 경우 우수한 아날로그를
쓸 수 있다.)
목록 6에서 다른 흥미로운 메소드는 factors()이며, 이는
필자의 "단계가 아니라 결과에 집중하자"는 충고를 실례로 보여준다. 숫자의 인수 발견하기의 문제점의 본질은 무엇인가? 다른 방식으로
서술하면, 최대 대상 숫자까지 모든 가능한 숫자 목록이 주어지면, 어느 것이 숫자의 인수인지 어떻게 판별하는가? 이는
필터링 연산을 제안한다 — 필자는 숫자의 전체 목록을 필터링할 수 있어 필자의 기준에 부합하지 않는
것을 제거한다. 해당 메소드는 기본적으로 이 설명과 같이 읽는다. 1에서부터 필자의 숫자까지 숫자 범위를
취한다(범위는 포괄적이지 않으므로 +1임). 즉, f() 메소드에서
코드를 기반으로 필터링하며, 이는 특정 데이터 유형으로 클래스를 작성하고 값을 리턴하도록 허용하는
Functional Java의 방식이다.
이 코드는 일반적으로 프로그래밍 언어에서 경향으로 더 큰 개념까지 실례로 보여준다. 과거에 개발자들은 메모리 할당, 가비지 콜렉션 및 포인터와 같은 모든 종류의 골치거리를 처리해야 했다. 시간이 흐르면서 언어가 이러한 것들에 더 많이 책임을 졌다. 컴퓨터가 점점 더 강력해지면서, 점점 더 지루한(자동화 가능) 작업을 언어와 런타임으로 떠넘겼다. Java 개발자로서 필자는 모든 메모리 문제를 언어로 양도하는 데 매우 익숙했다. 함수형 프로그래밍은 지시하는 포괄적인 더 구체적인 세부사항을 확장하고 있다. 시간이 흐르면서, 문제를 해결하고 프로세스의 측면에서 더 생각하는 데 필요한 단계에 대해 걱정할 시간이 줄어들 것이다. 이 시리즈를 진행하면서, 필자는 이러한 많은 예제를 보여줄 것이다.
함수형 프로그래밍은 특정 도구 세트 또는 언어보다는 사고방식에 더 가깝다. 필자는 이 첫 기사에서 함수형 프로그래밍의 일부 주제를 다루면서 시작하여, 이는 간단한 설계 의사결정에서부터 일부 야심찬 문제점 재고에까지 이른다. 필자는 간단한 Java 클래스를 다시 써서 더 함수적으로 만든 다음에, 함수형 프로그래밍을 기존의 명령적 언어를 사용하는 것과 따로 떨어져 설정하는 일부 주제로 파고들기 시작했다.
두 가지 중요한 장기적인 개념이 첫 번째로 여기에 나타났다. 첫 번째로 단계가 아니라 결과에 집중한다. 함수형 프로그래밍은 문제를 다르게 표현하려 시도한다. 왜냐하면 독자는 솔루션을 조성하는 다른 빌딩 블록을 보유하기 때문이다. 이 시리즈에 걸쳐서 두 번째로 필자가 보여줄 경향은 프로그래밍 언어와 런타임으로 지루한 세부사항의 이양이며, 이를 통해 프로그래밍 문제점의 고유한 측면에 집중할 수 있다. 다음 기사에서 필자는 함수형 프로그래밍의 일반적 측면과 오늘날 소프트웨어 개발로 이를 적용하는 방법에 대해 계속 살펴본다.
교육
- The Productive Programmer(Neal
Ford 저, O'Reilly Media, 2008년): Neal Ford의 가장 최신 저서에서 이 시리즈의 다양한 주제에 대해 확장한다.
- Monads:
함수형 언어에서 전설적으로 어려운 주제인 Monads를 이 시리즈의 다음 기사에서 다룰 것이다.
- Scala: Scala는 JVM에서
현대식의 함수형 언어이다.
- Clojure: Clojure는 JVM에서 실행하는 현대식의
함수형 Lisp이다.
- Podcast: Stuart Halloway on Clojure:
Clojure에 대해 더 많이 학습하고 빠르게 채택되어 인기도가 신속하게 올라가는 두 가지 주된 이유에 대해 알아보자.
- Akka: Akka는 정교한 작동자(actor) 기반
동시성을 허용하는 Java용 프레임워크이다.
- Functional Java: Functional Java는
많은 함수형 언어 구조를 Java로 추가하는 프레임워크이다.
- Design Patterns: Elements of Reusable Object-Oriented Software(Erich Gamma 등, Addison-Wesley, 1994년): 설계 패턴에 대한 Gang of Four의 고전 작품이다.
-
"Execution in the Kingdom of Nouns"(Steve Yegge, 2006년 3월):
Java 언어 설계의 일부 측면에 대해 재미있는 비판이다.
-
기술 서점에서
다양한 기술 주제와 관련된 서적을 살펴보자.
-
developerWorks Java 기술 영역: Java 프로그래밍과 관련된 모든 주제를 다루는 여러 편의 기사를 찾아보자.
토론
- developerWorks 커뮤니티에 참여하자.

Neal Ford는 글로벌 IT 컨설팅 업체인 ThoughtWorks의 소프트웨어 아키텍트이자 Meme Wrangler이다. 애플리케이션, 교육용 자료, 매거진 기사 및 비디오/DVD 프리젠테이션을 설계 및 개발하며 다양한 기술과 관련된 서적의 저자 또는 편집자이기도 하다. 최근에 출판된 책으로는 The Productive Programmer가 있다. 대규모 엔터프라이즈 애플리케이션의 설계 및 빌드에 많은 관심을 가지고 있는 그는 전세계의 개발자 컨퍼런스에서 국제적으로 인정 받고 있는 연사로도 활동하고 있다. 그의 웹 사이트를 살펴보자.