0. 개요
- 사용자의 요구사항은 시시각각 변함
- 그에 맞게 엔지니어링을 꾸준히 변화해야 하는데, 비용을 최소화하면 좋음
- 동작 파라미터화를 통해 자주 바뀌는 요구사항에 대해 대응할 수 있도록 함
- 메소드의 인수로 코드 블록을 전달함으로써 ‘동작’ 자체를 인수로 전달할 수 있음.
- 리스트를 예로 들면, 아래처럼 진행 가능함
- 리스트의 모든 요소에 대해서 ‘어떤 동작’을 수행
- 리스트 관련 작업을 끝낸 다음 ‘어떤 다른 동작’ 수행
- 에러가 발생하면 ‘정해진 어떤 동작’을 수행
1. 변화하는 요구사항에 대응
- 예컨데, “필터링”하는 작업에서 기준이 달라지는 경우가 있다.
- 현재는 녹색사과만 선택(필터링)하고 있다.
- 만약, 빨간사과도 필터링하고자 하면 어떻게 고칠까??
public static List<Apple> filterRedApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>(); //<——| 사과누적 리스트
for (Apple apple: inventory) {
if (RED.equals(apple.getColor()) { //<-—-| 녹색 사과만 선택
result.add(apple);
}
}
return result;
}
- 위처럼 할 수 있지만, 더 좋은 방법이 있다.
비슷한 코드가 반복 존재한다면 그 코드를 추상화한다.
- 추상화 방법의 첫번째로 색을 파라미터화 해주는 것이다.
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if ( apple.getColor().equals(color) ) {
result.add(apple);
}
}
reurn result;
}
- 이러면 색을 기준으로 필터링 하는 것은 하나의 메소드를 재활용해서 사용할 수 있게 되었다.
- 근데, 기준이 색에서 무게로 바뀐다면 이 메소드를 사용할 수 없을 것이다.
- 가장 심플한 방법은 메소드를 새로 만드는 것이다.
filterApplesByWeight(List<Apple> inventory, int weight)
- 물론, 해결되고 좋은 방법일 수 있지만, 이는 대부분 중복된 것을 또 만드는 결과를 초래한다.
- 그러나, 소프트웨어 공학의 DRY(Don’t Repeat Yourself) 원칙을 어기는 행위이다.
- 다른 방법으로는 두가지 메소드를
filter(List<Apple> inventory, Color color, int weight, boolean flag)
로 합칠 수도 있다. - 그러나, flag의 값이 무엇을 의미하는 지 알 수 없으며, 요구사항이 바뀌면 유연하게 대응할 수도 없다.
- 그렇기 때문에 우리는 동작 파라미터화를 이용할 것이다.
- 가장 심플한 방법은 메소드를 새로 만드는 것이다.
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color)
{
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if ( apple.getColor().equals(color) ) {
result.add(apple);
}
}
ret니rn result;
}
2. 동작 파라미터화
- 참 또는 거짓을 반환하는 함수를 Predicate라고 한다.
- 이를 위한 인터페이스를 정의해본다.
- 인터페이스를 이용해서 다양한 선택 조건을 대표하는 여러 버전의 ApplePredicate를 정의할 수 있다.
public interface ApplePredicate{
boolean test(Apple apple);
}
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return apple.getWeight() > 150;
}
}
public class AppleGreenWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return GREEN.equals(apple.getColor());
}
}
- 위는 일종의 전략 디자인 패턴을 사용한 것이다.
- 그렇다면, 어떻게 동작할 수 있을까?
- filterApples 메소드에서 ApplePredicate 객체를 받아 애플의 조건을 검사하도록 메서드를 고쳐야 한다.
- 이것이 동작 파라미터화이며, 메서드가 다양한 동작을 받아서 내부적으로 다양한 동작을 수행할 수 있다.
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> res나It = new ArrayList<>();
for(Apple apple: inventory) {
if(p.test(apple)) { // predicate 객체로 사과 검사 조건을 캡슐화하였다.
result.add(apple);
}
}
return result;
}
- 이건 아주 효과적일거다. 요구사항이 바뀌면(색, 무게, 색과 무게 모두, 기타 등등) 모두 새롭게 ApplePredicate를 만들어서 전달해주면 된다.
- 약간의 아쉬운 점이라면, 하나의 메소드(test메소드)를 사용하기 위해서 객체를 만들어야 한다. 이는 추후에 메서드로 인자를 넘기는 방법을 알아보며 해결해보자
3. 익명 클래스를 통한 간소화
- 2.2에서 제안한 방법도 좋은 방법이지만, 매번 클래스를 만들고 이를 인스턴스화하여 인자로 넘기는 것은 귀찮은 일이기도 하다.
- 이를 개선하기 위해서 자바는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스라는 기법을 제공한다.
- GUI 환경에서 이벤트 핸들러 객체를 만들 때 종종 사용되는 테크닉이다.
- 그럼에도 몇가지 문제가 있다.
- 중복되는 코드가 아직 많이 남아있다.
- 많은 프로그래머가 익명 클래스의 사용에 익숙치 않다.
//메서드의 동작을 직접 파라미터화하였다.
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
public boolean test(Apple apple){
return RED. equals(apple. getColor());
}
});
- 자바 8에서는 더 좋은 해결책으로 람다 표현식을 사용할 수도 있다.
- 또, 리스트형식으로 추상화할 수 있다.(제너릭활용)
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;
}
4. 실전 예제: Comparator, Runnable, GUI
- 이미 자바API에서 많은 것들을 동작 파라미터화하여 제공하고 있다.
- Comparator로 정렬, Runnable 코드 블록 실행, Callable로 결과 반환, GUI 이벤트 등이 있다.
1. Comparator
- 컬렉션 정렬을 위해 사용된다.
- List의 sort()메소드 혹은 Collections.sort() 메소드를 사용한다.
- 이때, Comparator를 활용해서 동작을 파라미터화하여 정렬의 규칙을 생성할 수 있다.
- 요구사항에 맞게 정렬 방식을 정할 수 있으며, 람다 표현식으로도 가능하다.
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
});
inventory.sort(
(Apple a1, Apple a2) -> a1.getWeight(),compareTo(a2.getWeight()));
2. Runnable
- 자바 스레드를 활용하여 병렬로 코드 블록을 실행할 수 있다.
- 이를 위해, Thread 객체 생성이 필요하고, 생성자에는 객체만을 전달할 수 있어서, Runnable 인터페이스를 구현하여 전달하였다.
3. Callable
- 자바 5에서 지원하는 ExecutorService를 이용해서 태스크를 스레드 풀로 보내고, 결과를 Future에 저장할 수 있다.
4. GUI 이벤트 처리하기
- GUI 환경에서 마우스 클릭이나 문자열 위로 이동하는 등의 이벤트에 대응하는 동작을 수행하는 식으로 동작한다.
- 그래서, 한가지 동작에 대해 수행해야할 로직이 다르게 정의되는 경우가 많기에 유연한 코드가 필요하다.
'Backend > JAVA' 카테고리의 다른 글
[JAVA] default 메소드에 대하여(모던 자바 인 액션) (0) | 2024.04.10 |
---|---|
[JAVA] Object 클래스와 Object 메소드(equals, hashCode, toString) (4) | 2023.11.06 |
[JAVA] Servlet 이란? (0) | 2023.08.20 |
[JAVA] JAVA 기본 용어 정리 (0) | 2023.07.31 |
0. 개요
- 사용자의 요구사항은 시시각각 변함
- 그에 맞게 엔지니어링을 꾸준히 변화해야 하는데, 비용을 최소화하면 좋음
- 동작 파라미터화를 통해 자주 바뀌는 요구사항에 대해 대응할 수 있도록 함
- 메소드의 인수로 코드 블록을 전달함으로써 ‘동작’ 자체를 인수로 전달할 수 있음.
- 리스트를 예로 들면, 아래처럼 진행 가능함
- 리스트의 모든 요소에 대해서 ‘어떤 동작’을 수행
- 리스트 관련 작업을 끝낸 다음 ‘어떤 다른 동작’ 수행
- 에러가 발생하면 ‘정해진 어떤 동작’을 수행
1. 변화하는 요구사항에 대응
- 예컨데, “필터링”하는 작업에서 기준이 달라지는 경우가 있다.
- 현재는 녹색사과만 선택(필터링)하고 있다.
- 만약, 빨간사과도 필터링하고자 하면 어떻게 고칠까??
public static List<Apple> filterRedApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>(); //<——| 사과누적 리스트
for (Apple apple: inventory) {
if (RED.equals(apple.getColor()) { //<-—-| 녹색 사과만 선택
result.add(apple);
}
}
return result;
}
- 위처럼 할 수 있지만, 더 좋은 방법이 있다.
비슷한 코드가 반복 존재한다면 그 코드를 추상화한다.
- 추상화 방법의 첫번째로 색을 파라미터화 해주는 것이다.
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if ( apple.getColor().equals(color) ) {
result.add(apple);
}
}
reurn result;
}
- 이러면 색을 기준으로 필터링 하는 것은 하나의 메소드를 재활용해서 사용할 수 있게 되었다.
- 근데, 기준이 색에서 무게로 바뀐다면 이 메소드를 사용할 수 없을 것이다.
- 가장 심플한 방법은 메소드를 새로 만드는 것이다.
filterApplesByWeight(List<Apple> inventory, int weight)
- 물론, 해결되고 좋은 방법일 수 있지만, 이는 대부분 중복된 것을 또 만드는 결과를 초래한다.
- 그러나, 소프트웨어 공학의 DRY(Don’t Repeat Yourself) 원칙을 어기는 행위이다.
- 다른 방법으로는 두가지 메소드를
filter(List<Apple> inventory, Color color, int weight, boolean flag)
로 합칠 수도 있다. - 그러나, flag의 값이 무엇을 의미하는 지 알 수 없으며, 요구사항이 바뀌면 유연하게 대응할 수도 없다.
- 그렇기 때문에 우리는 동작 파라미터화를 이용할 것이다.
- 가장 심플한 방법은 메소드를 새로 만드는 것이다.
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color)
{
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if ( apple.getColor().equals(color) ) {
result.add(apple);
}
}
ret니rn result;
}
2. 동작 파라미터화
- 참 또는 거짓을 반환하는 함수를 Predicate라고 한다.
- 이를 위한 인터페이스를 정의해본다.
- 인터페이스를 이용해서 다양한 선택 조건을 대표하는 여러 버전의 ApplePredicate를 정의할 수 있다.
public interface ApplePredicate{
boolean test(Apple apple);
}
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return apple.getWeight() > 150;
}
}
public class AppleGreenWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return GREEN.equals(apple.getColor());
}
}
- 위는 일종의 전략 디자인 패턴을 사용한 것이다.
- 그렇다면, 어떻게 동작할 수 있을까?
- filterApples 메소드에서 ApplePredicate 객체를 받아 애플의 조건을 검사하도록 메서드를 고쳐야 한다.
- 이것이 동작 파라미터화이며, 메서드가 다양한 동작을 받아서 내부적으로 다양한 동작을 수행할 수 있다.
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> res나It = new ArrayList<>();
for(Apple apple: inventory) {
if(p.test(apple)) { // predicate 객체로 사과 검사 조건을 캡슐화하였다.
result.add(apple);
}
}
return result;
}
- 이건 아주 효과적일거다. 요구사항이 바뀌면(색, 무게, 색과 무게 모두, 기타 등등) 모두 새롭게 ApplePredicate를 만들어서 전달해주면 된다.
- 약간의 아쉬운 점이라면, 하나의 메소드(test메소드)를 사용하기 위해서 객체를 만들어야 한다. 이는 추후에 메서드로 인자를 넘기는 방법을 알아보며 해결해보자
3. 익명 클래스를 통한 간소화
- 2.2에서 제안한 방법도 좋은 방법이지만, 매번 클래스를 만들고 이를 인스턴스화하여 인자로 넘기는 것은 귀찮은 일이기도 하다.
- 이를 개선하기 위해서 자바는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스라는 기법을 제공한다.
- GUI 환경에서 이벤트 핸들러 객체를 만들 때 종종 사용되는 테크닉이다.
- 그럼에도 몇가지 문제가 있다.
- 중복되는 코드가 아직 많이 남아있다.
- 많은 프로그래머가 익명 클래스의 사용에 익숙치 않다.
//메서드의 동작을 직접 파라미터화하였다.
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
public boolean test(Apple apple){
return RED. equals(apple. getColor());
}
});
- 자바 8에서는 더 좋은 해결책으로 람다 표현식을 사용할 수도 있다.
- 또, 리스트형식으로 추상화할 수 있다.(제너릭활용)
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;
}
4. 실전 예제: Comparator, Runnable, GUI
- 이미 자바API에서 많은 것들을 동작 파라미터화하여 제공하고 있다.
- Comparator로 정렬, Runnable 코드 블록 실행, Callable로 결과 반환, GUI 이벤트 등이 있다.
1. Comparator
- 컬렉션 정렬을 위해 사용된다.
- List의 sort()메소드 혹은 Collections.sort() 메소드를 사용한다.
- 이때, Comparator를 활용해서 동작을 파라미터화하여 정렬의 규칙을 생성할 수 있다.
- 요구사항에 맞게 정렬 방식을 정할 수 있으며, 람다 표현식으로도 가능하다.
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
});
inventory.sort(
(Apple a1, Apple a2) -> a1.getWeight(),compareTo(a2.getWeight()));
2. Runnable
- 자바 스레드를 활용하여 병렬로 코드 블록을 실행할 수 있다.
- 이를 위해, Thread 객체 생성이 필요하고, 생성자에는 객체만을 전달할 수 있어서, Runnable 인터페이스를 구현하여 전달하였다.
3. Callable
- 자바 5에서 지원하는 ExecutorService를 이용해서 태스크를 스레드 풀로 보내고, 결과를 Future에 저장할 수 있다.
4. GUI 이벤트 처리하기
- GUI 환경에서 마우스 클릭이나 문자열 위로 이동하는 등의 이벤트에 대응하는 동작을 수행하는 식으로 동작한다.
- 그래서, 한가지 동작에 대해 수행해야할 로직이 다르게 정의되는 경우가 많기에 유연한 코드가 필요하다.
'Backend > JAVA' 카테고리의 다른 글
[JAVA] default 메소드에 대하여(모던 자바 인 액션) (0) | 2024.04.10 |
---|---|
[JAVA] Object 클래스와 Object 메소드(equals, hashCode, toString) (4) | 2023.11.06 |
[JAVA] Servlet 이란? (0) | 2023.08.20 |
[JAVA] JAVA 기본 용어 정리 (0) | 2023.07.31 |