일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- linebot
- spring
- springboot
- reflection
- Visitor pattern
- Optional
- line messaging api
- getOrCreate
- NullPointerException
- 챗봇
- Modelmapper
- mapping
- DtoToEntity
- webhook
- Dynamic dispatch
- Controller
- visitor proxy pattern
- static dispatch
- double dispatch
- java
- EntityToDto
- ngrok
- enum
- annotation
- 토비의 봄
- Today
- Total
database by narae :p
더블 디스패치 double dispatch 본문
의존 관계
Supplier의 변화가 Client에 영향을 주는 경우
- Supplier가 Client의 필드
- Supplier가 Client 메소드의 파라미터
- Supplier가 Client의 로컬 변수
- Supplier로 메시지를 보냄
=> 재사용 가능한 객체 지향 설계/개발이 어렵다.
Client는 재사용이 어렵다
Client는 컴포넌트/서비스가 될 수 없다.
오브젝트 패턴은 런타임시 바뀔 수 있는, (상속 관계보다) 더 동적인 오브젝트 (의존) 관계를 다룬다.
- 생성 관련 패턴 (Creational Pattern) : 객체 인스턴스 생성을 위한 패턴으로, 클라이언트와 그 클라이언트에서 생성해야 할 객체 인스턴스 사이의 연결을 끊어주는 패턴
싱글턴, 팩토리 메소드, 추상 팩토리, 프로토타입, 빌더 패턴
- 행동 관련 패턴 (Behavioral Pattern) : 클래스와 객체들이 상호작용하는 방법 및 역할을 분담하는 방법과 관련된 패턴
스트래티지, 옵저버, 스테이트, 커맨드, 이터레이터, 템플릿 메소드, 인터프리터, 미디에이터, 역할 변경, 메멘토, 비지터
- 구조 관련 패턴 (Structural Pattern) : 클래스 및 객체들을 구성을 통해서 더 큰 구조로 만들 수 있게 해 주는 것과 관련된 패턴
데코레이터, 어댑터, 컴포지트, 퍼사드, 프록시, 브리지, 플라이웨이트
Method dispatch
어떤 메소드를 실행할지 결정하는 것
# static dispatch
class Service {
void run() {
System.out.println("run()");
}
}
public static void main(String[] args) {
new Service().run();
}
컴파일 되는 시점에 컴파일러가 어떤 클래스의 메소드를 수행하는지 알고 있고 바이트 코드도 남는다.
# dynamic dispatch
abstract class Service {
abstract void run();
}
class ServiceImpl extends Service {
void run() {
System.out.println("run()");
}
}
public static void main(String[] args) {
Service service = new ServiceImpl();
service.run();
}
어떤 메소드를 실행하는지 컴파일 시점에 모름. 추상클래스의 메소드를 호출하는 것만 알고 있음.
런타임 시점에 service에 할당된 객체가 무엇인지 확인하고 메소드를 실행함
Double Dispatch
Dynamic Dispatch를 두 번 하는 것
* 예시. SNS, Post
1. 구현체에 따라 로직이 다르지 않은 경우
interface Post {
void postOn(SNS sns);
}
class Text implements Post {
public void postOn(SNS sns) {
// text -> sns
}
}
class Picture implements Post {
public void postOn(SNS sns) {
// picture -> sns
}
}
interface SNS {};
class Facebook implements SNS {
}
class Twitter implements SNS {
}
public static void main(String[] args) {
List<Post> posts = Arrays.asList(new Text(), new Picture());
List<SNS> sns = Arrays.asList(new Facebook(), new Twitter());
posts.forEach(p -> sns.forEach(s -> p.postOn(s)));
}
SNS의 구현체에 따라 로직이 달라지는 경우를 고려하지 않음
2. SNS의 구현체에 따라 로직이 다른 경우 (분기문 사용)
interface Post {
void postOn(SNS sns);
}
class Text implements Post {
public void postOn(SNS sns) {
if(sns instanceof Facebook) {
// text -> facebook
} else if(sns instanceof Twitter) {
// text -> twitter
} else {
throw new IllegalArgumentException();
}
}
}
class Picture implements Post {
public void postOn(SNS sns) {
if(sns instanceof Facebook) {
// picture -> facebook
} else if(sns instanceof Twitter) {
// picture -> twitter
} else {
throw new IllegalArgumentException();
}
}
}
interface SNS {};
class Facebook implements SNS {
}
class Twitter implements SNS {
}
- SNS의 새로운 구현체가 생기면 분기문을 추가해야 함
- 만약 실수로 분기문을 추가하지 않으면 의도치 않게 exception 발생
3. SNS의 구현체에 따라 로직이 다른 경우 (메소드 오버로딩 사용. static dispatch)
interface Post {
void postOn(SNS sns);
}
class Text implements Post {
public void postOn(Facebook facebook) {
// text -> facebook
}
public void postOn(Twitter twitter) {
// text -> twitter
}
}
class Picture implements Post {
public void postOn(Facebook facebook) {
// picture -> facebook
}
public void postOn(Twitter twitter) {
// picture -> twitter
}
}
interface SNS {};
class Facebook implements SNS {
}
class Twitter implements SNS {
}
이전 코드와 달리 메소드 오버로딩을 사용해 분기문을 제거함
하지만 다음 코드 중 s -> p.postOn(s) 에서 컴파일 시점에 에러 발생
public static void main(String[] args) {
List<Post> posts = Arrays.asList(new Text(), new Picture());
List<SNS> sns = Arrays.asList(new Facebook(), new Twitter());
posts.forEach(p -> sns.forEach(s -> p.postOn(s)));
}
- 메소드 오버로딩은 static dispatch 이므로 컴파일 시점에 어떤 클래스의 메소드를 수행할지 알아야 함
- 하지만 s는 SNS라는 interface의 타입이기 때문에 어떤 구현체(Facebook, Twitter 등)의 타입인지 컴파일러가 알 수 없음
오류 발생. 실패 !!!!!!!
Double Dispatch 사용
interface Post {
void postOn(SNS sns);
}
class Text implements Post {
public void postOn(SNS sns) {
sns.post(this);
}
}
class Picture implements Post {
public void postOn(SNS sns) {
sns.post(this);
}
}
interface SNS {
void post(Text text);
void post(Picture picture);
}
class Facebook implements SNS {
public void post(Text text) {
// text -> facebook
}
public void post(Picture picture) {
// picture -> facebook
}
}
class Twitter implements SNS {
public void post(Text text) {
// text -> twitter
}
public void post(Picture picture) {
// picture -> twitter
}
}
public static void main(String[] args) {
List<Post> posts = Arrays.asList(new Text(), new Picture());
List<SNS> sns = Arrays.asList(new Facebook(), new Twitter());
posts.forEach(p -> sns.forEach(s -> p.postOn(s)));
}
- 한 단계를 더 거친 것 같지만, 분기문을 사용하지 않고 dynamic dispatch를 두 번 사용
- Post 중 어떤 구현체의 postOn 메소드를 실행할지 dynamic dispatch 한 번 사용
- postOn 메소드 내부에서 SNS 중 어떤 구현체의 post 메소드를 실행할지 dynamic dispatch 한 번 사용
=> 새로운 구현체가 생기는 경우에는 다음과 같이 구현체에 대한 코드만 작성하면 됨
class Instagram implements SNS {
public void post(Text text) {
// text -> instagram
}
public void post(Picture picture) {
// picture -> instagram
}
}
- 구현체를 새로 추가하는 것이 자유로움 => 기존에 의존하던 코드에 직접적으로 영향을 주지 않는다.
=> 비지터 패턴으로 예를 들면, SNS가 visitor 역할을 하고 Post의 메소드 postOn()이 accept() 역할
Visitor Pattern
위의 코드로 예를 들면,
- Post는 SNS 타입의 어떤 구현체가 들어오는지 관심 없고, postOn() 메소드 (accept) 를 제공
- SNS의 구현체(visistor)의 post() 메소드 (visit) 를 통해 실제 로직을 실행할 수 있음
- SNS의 구현체가 새로 추가되어도 Post에는 영향을 미치지 않음
- Post의 구현체가 추가된다면 ? 어쩔 수 없이 SNS에 Post의 구현체를 처리하는 메소드를 다 추가해 줘야 함. SNS의 구현체에 따라 로직이 다르지 않고 모두 공통 로직을 사용한다면 SNS를 abstract class로 작성해서 사용해도 됨.
Visitor Proxy Pattern (hibernate)
* polimophic query
List<SNS> sns = repository.findSNS();
이 list의 요소들을 instanceof로 타입을 체크하면 실패한다.
- JPA에서 각각의 요소들은 프록시 구조로 반환되기 때문에 타입은 SNS 타입이기 때문.
- 따라서, 이 때는 반드시 visitor 패턴을 사용해야 함.
=> 따라서, 프록시를 visitor로 해서 타입을 체크할 수 있다. 이를 일반화해서 proxy visitor pattern 이라고 함
'개발 노트' 카테고리의 다른 글
도커 + ELK 분산 환경 셋팅 (0) | 2020.05.24 |
---|---|
도커 Docker 정리 1 (0) | 2020.05.16 |
user level lock (0) | 2019.06.12 |
JPA 정리하기 (0) | 2019.06.07 |
Annotation과 Reflection을 이용한 챗봇 컨트롤러 만들기 (0) | 2019.06.07 |