방문자 (visitor pattern)
- Visitor pattern 은 데이터와 구조를 분리합니다.
- 데이터 구조 안을 돌아다니는 주체인 방문자를 나타내는 클래스를 준비해서 처리에 맡긴다.
- 새로운 처리를 추가하고 싶을 때 새로운 방문자를 만들고 데이터 구조는 방문자를 받아들이면 된다.
- OCP(개방-폐쇠)원칙을 적용하는 방법중 하나
- 확장에는 열려있음
- 클래스를 설계할 때, 특별한 이유가 없는 한 확장을 금지 해서는 안됨
- 수정에는 닫혀있음
- 즉 확장을 할 때 기존 클래스를 수정하면 안됨.
- 실제 로직을 가지고 있는 객체 (Visitor)가 로직을 적용할 객체(Element)를 방문하면서 실행하는 패턴.
- 아래 예제에서 Element는 item 입니다.
- 확장에는 열려있음
디스패치란?
- 어떤 메소드를 호출할 것인가를 결정하고 결정된 메소드를 실행하는 과정(행위)
정적 디스패치
- 컴파일 시점에 특정 메소드를 호출할 것이라고 미리 명확하게 알고 있는 것
- 메소드 오버로딩
- 메소드 시그니쳐
- 동작 방식
- 컴파일 전 단계에서 인자의 타입을 알수있기 때문에 오버라이드로 구현만 하면된다.
동적 디스패치
- 정적 디스패치와 반대로 컴파일러가 어떤 메서드를 호출하는지 알수 없습니다.
- 동적 디스패치는 호출할 메서드를 런타임 시점에서 결정한다
- 동작 방식
- 객체 내에 this를 던져줄수 있는 메서드를 만들어 인자로 visitor를 받는다.
- visitor에서 this를 받아 그에 오버라이드 된 메서드를 선택하여 처리한다.
lambda
- interface 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum Operator{
ADD,SUB
}
interface Calcurator{
int calc(int a, int b, Operator operator);
}
public class Example02 {
public static void main(String[] args) {
int a = 100;
int b =10;
Calcurator calcurator = (int x, int y, Operator operator) -> {
switch (operator){
case ADD:
return x+y;
case SUB:
return x-y;
default:
return 0;
}
};
System.out.println("더하기:" + calcurator.calc(a,b,Operator.ADD));
System.out.println("빼기:" + calcurator.calc(a,b,Operator.SUB));
}
}
위와 같이 인터페이스를 생성하고 lambda로 구현하는 방법도 있다.
- Supplier< T >
- 파라미터 없이 반환 값만 갖는 함수형 인터페이스이다.
1 2 3 4 5 6 7
public class Supplier { public static void main(String[] args) { //TODO Supplier Supplier<User> supplier = () -> new User("Sangwoong",25); System.out.println(supplier.get()); } }
- 파라미터 없이 반환 값만 갖는 함수형 인터페이스이다.
위와 같이 똑같은 객체를 반복 생성 해야할 때 사용할 수 있다.
- Consumer< T >
- T를 입력 받아서 처리하는 함수 변환 값은 없다.
1
2
3
4
5
6
7
8
9
10
public class Consumer {
public static void main(String[] args) {
User user = new User("홍길동", 25);
//TODO Consumer
Consumer<User> consumer1 = (User inputUser) -> inputUser.toString();
consumer1.accept(user);
Consumer<User> consumer2 = User::toString;
consumer2.accept(user);
}
}
위와 같이 User 를 받아 toString을 호출하는 Consumer 를 구현한다고 하면 Consumer2 처럼 :: 을 통해 간단하게 작성할 수 있다.
- Function<T,R>
- T를 입력받아 R를 리턴하는 함수
1
2
3
4
5
6
7
8
9
10
11
12
public class Function {
public static void main(String[] args) {
User user = new User("홍길동", 25);
//TODO Function
List<User> users = new ArrayList<>();
Function<List,Integer> function1 = (List lists) -> lists.size();
System.out.println(function1.apply(users));
users.add(user);
Function<List, Integer> function2 = List::size;
System.out.println(function2.apply(users));
}
}
위와 같이 User 를 받아 size() 호출하는 Function 를 구현한다고 하면 Function2 처럼 :: 을 통해 간단하게 작성할 수 있다.
- Predicate
- T를 매개 변수로 받아 boolean 을 리턴한다.
1
2
3
4
5
6
7
8
public class Predicate {
public static void main(String[] args) {
User user = new User("홍길동", 25);
//TODO Predicate
Predicate<User> predicate = (User predicateUser) -> predicateUser.getAge() > 25;
System.out.println(predicate.test(user));
}
}
위와 같이 User를 받아서 나이가 25보다 높으면 true 아니면 false 를 출력하게 작성할 수 있다.
Effectively final 이란?
- final이 붙지 않은 변수의 값이 사실상 변경되지 않는다면. 그 변수는 Effectively final ( 사실상 final ) 이라고 합니다.
- java8에서 도입 되었으며 익명 클래스 또는 람다식이 사용된 코드에서 쉽게 찾아 볼 수 있습니다.