Double Dispatch를 학습하기 전 Method Dispatch가 무엇인지 잘 모르신다면 아래 글을 참고해주세요.
https://ttl-blog.tistory.com/776
Double Dispatch
Dynamic Method Dispatch를 2번 한다는 의미입니다.
Dynamic Method Dispatch를 2번 한다는 것은,
실행 시점에 호출할 메서드를 결정하고 실행하는 과정을 2번에 걸쳐서 진행한다는 의미입니다.
코드를 통해 자세히 살펴보겠습니다.
import java.util.List;
public class Dispatch {
interface Post {
void postOn(SNS sns);
}
static class Text implements Post {
@Override
public void postOn(SNS sns) {
System.out.println("Text PostOn [" + sns.getClass().getSimpleName() +"]");
}
}
static class Picture implements Post {
@Override
public void postOn(SNS sns) {
System.out.println("Picture PostOn [" + sns.getClass().getSimpleName() +"]");
}
}
interface SNS {}
static class Facebook implements SNS {}
static class Instagram implements SNS {}
public static void main(String[] args) {
List<Post> posts = List.of(new Text(), new Picture());
List<SNS> snss = List.of(new Facebook(), new Instagram());
posts.forEach(post -> snss.forEach(post::postOn));
}
}
두 개의 인터페이스 Post와 SNS를 정의하였습니다.
Post는 postOn 메서드를 통해 SNS에 Post를 게시합니다.
위 코드의 실행 결과는 다음과 같습니다.
위 경우에는 아무런 문제가 발생하지 않습니다.
이제 위 코드에 postOn으로 들어오는 SNS의 종류에 따라 다른 작업을 실행해 보도록 하겠습니다.
import java.util.List;
import java.util.stream.IntStream;
/**
* Created by ShinD on 2022/07/08.
*/
public class Dispatch {
interface Post {
void postOn(SNS sns);
}
static class Text implements Post {
@Override
public void postOn(SNS sns) {
if (sns instanceof Facebook) {
System.out.println("Text postOn [Facebook]");
}
if (sns instanceof Instagram) {
System.out.println("Text postOn [Instagram]");
}
}
}
static class Picture implements Post {
@Override
public void postOn(SNS sns) {
if (sns instanceof Facebook) {
System.out.println("Picture postOn [Facebook]");
}
if (sns instanceof Instagram) {
System.out.println("Picture postOn [Instagram]");
}
}
}
interface SNS {}
static class Facebook implements SNS {}
static class Instagram implements SNS {}
public static void main(String[] args) {
List<Post> posts = List.of(new Text(), new Picture());
List<SNS> snss = List.of(new Facebook(), new Instagram());
posts.forEach(post -> snss.forEach(post::postOn));
}
}
위 코드 역시 결과를 출력하는 것에는 문제가 없습니다.
그러나 이는 SNS의 새로운 구현체가 등장하였을 경우 문제가 발생합니다.
SNS에 Twitte를 추가하고, main에서의 SNS 리스트에 Twiter를 추가해 보도록 하겠습니다.
import java.util.List;
import java.util.stream.IntStream;
/**
* Created by ShinD on 2022/07/08.
*/
public class Dispatch {
//... 동일
interface SNS {}
static class Facebook implements SNS {}
static class Instagram implements SNS {}
static class Twitter implements SNS {}
public static void main(String[] args) {
List<Post> posts = List.of(new Text(), new Picture());
List<SNS> snss = List.of(new Facebook(), new Instagram(), new Twitter());
posts.forEach(post -> snss.forEach(post::postOn));
}
}
위 코드의 실행 결과는 다음과 같습니다.
분명 Twitter가 추가되었으나, 이를 Post에서 처리할 수 없기에 Twitter에 대해서는 아무 작업이 수행되지 않습니다.
즉 이를 해결하기 위해서는 SNS 타입의 새로운 구현체가 정의될 때마다 if문을 추가해 주어야 합니다.
SNS가 추가될 때마다 기존의 Post의 코드가 바뀌는 것은 OCP를 위반합니다.
이제부터 이를 해결하는 방법을 알아보도록 하겠습니다.
1. Post의 postOn 메서드의 파라미터를 구현체로 구체화하여 정의한다.
우선 이 방법은 해결책이 될 수 없음을 미리 이야기하고 살펴보겠습니다.
이 방법을 적용한 Post 코드는 다음과 같습니다.
interface Post {
void postOn(Facebook sns);
void postOn(Instagram sns);
}
static class Text implements Post {
@Override
public void postOn(Facebook sns) {
System.out.println("Text postOn [Facebook]");
}
@Override
public void postOn(Instagram sns) {
System.out.println("Text postOn [Instagram]");
}
}
static class Picture implements Post {
@Override
public void postOn(Facebook sns) {
System.out.println("Picture postOn [Facebook]");
}
@Override
public void postOn(Instagram sns) {
System.out.println("Picture postOn [Instagram]");
}
}
이는 새로운 SNS 구현체가 등장하였을 때, 기존 코드를 수정하는 것이 아닌 해당 구현체에 대한 postOn을 추가하여 주면 되기 때문에 OCP를 위반하지 않습니다.
그러나 이는 해결방법이 될 수 없습니다.
다음과 같이 다형성을 이용할 수 없어지기 때문입니다.
2. Double Dispatch 사용
import java.util.List;
public class Dispatch {
interface Post {
void postOn(SNS sns);
}
static class Text implements Post {
@Override
public void postOn(SNS sns) {
sns.post(this);
}
}
static class Picture implements Post {
@Override
public void postOn(SNS sns) {
sns.post(this);
}
}
interface SNS {
void post(Text post);
void post(Picture post);
}
static class Facebook implements SNS {
@Override
public void post(Text post) {
System.out.println("Text postOn [Facebook]");
}
@Override
public void post(Picture post) {
System.out.println("Picture postOn [Facebook]");
}
}
static class Instagram implements SNS {
@Override
public void post(Text post) {
System.out.println("Text postOn [Facebook]");
}
@Override
public void post(Picture post) {
System.out.println("Picture postOn [Facebook]");
}
}
public static void main(String[] args) {
List<Post> posts = List.of(new Text(), new Picture());
List<SNS> snss = List.of(new Facebook(), new Instagram());
posts.forEach(post -> snss.forEach(post::postOn));
}
}
위 코드에서는 다음과 같은 Dynamic Dispatch가 2번 발생합니다.
1. post의 postOn 실행 시 Dynamic Dispatch가 1번 발생합니다.
2. postOn 메서드 내부에서 sns의 post 실행 시 Dynamic Dispatch가 1번 발생합니다.
이처럼 Dynamic Dispatch가 2번 진행되는 것을 Double Dispatch라 합니다.
두가지 이상의 하위타입을 갖는 타입들을 조합하여 이차원적인 비즈니스 로직 구조가 만들어지는 경우에는
위처럼 Double Dispatch를 사용하여 해결할 수 있습니다.
Double Dispatch와 Visitor Pattern
Double Dispatch는 Visitor Pattern보다 더 일반화된(추상적인) 형태라고 볼 수 있습니다.
기능은 똑같이 두고, 메서드와 클래스의 이름만 조금 변경하여 Visitor Pattern과 비슷하게 변경해보면 다음과 같습니다.
interface Post {
void accept(PostVisitor visitor);
}
static class Text implements Post {
@Override
public void accept(PostVisitor visitor) {
visitor.visit(this);
}
}
static class Picture implements Post {
@Override
public void accept(PostVisitor visitor) {
visitor.visit(this);
}
}
interface PostVisitor {
void visit(Text post);
void visit(Picture post);
}
static class PostOnFacebookVisitor implements PostVisitor {
@Override
public void visit(Text post) {
System.out.println("Text postOn [Facebook]");
}
@Override
public void visit(Picture post) {
System.out.println("Picture postOn [Facebook]");
}
}
static class PostOnInstagramVisitor implements PostVisitor {
@Override
public void visit(Text post) {
System.out.println("Text postOn [Facebook]");
}
@Override
public void visit(Picture post) {
System.out.println("Picture postOn [Facebook]");
}
}
public static void main(String[] args) {
List<Post> posts = List.of(new Text(), new Picture());
List<PostVisitor> postVisitors = List.of(new PostOnFacebookVisitor(), new PostOnInstagramVisitor());
posts.forEach(post -> postVisitors.forEach(post::accept));
}
Reference
https://www.youtube.com/watch?v=s-tXAHub6vg&list=PLv-xDnFD-nnmof-yoZQN8Fs2kVljIuFyC&index=16
'☕️ Java > 기본' 카테고리의 다른 글
[Java] Thread (2) - 쓰레드 구현과 실행 (0) | 2022.07.09 |
---|---|
[Java] Thread (1) - 프로세스와 쓰레드 (0) | 2022.07.09 |
[Java] Method Dispatch란? (Static, Dynamic) (0) | 2022.07.08 |
[Java] 리플렉션을 활용한 백준 자동 README 생성기 (0) | 2022.02.07 |
[Java] Method Signature와 Method Type (0) | 2022.02.06 |