이중 디스패치

Double dispatch

소프트웨어 엔지니어링에서 더블 디스패치는 특수형태의 복수 디스패치이며 콜에 관련된2개의 오브젝트의 런타임유형에 따라 함수 콜을 다른 구체적인 함수로 디스패치하는 메커니즘입니다.대부분의 객체 지향 시스템에서 코드 내의 함수 호출에서 호출되는 구체적인 함수는 단일 객체의 동적 유형에 따라 달라지기 때문에 단일 디스패치 호출 또는 단순히 가상 함수 호출로 알려져 있습니다.

Dan Ingalls는 Smalltalk에서 이중 디스패치를 사용하는 방법을 다중 다형이라고 [1]처음 설명했습니다.

개요

일반적으로 해결되는 문제는 수신자뿐만 아니라 인수에 따라 메시지를 다른 방법으로 디스패치하는 방법입니다.

이를 위해 CLOS 등의 시스템여러 디스패치를 구현합니다.이중 디스패치는 다중 디스패치를 지원하지 않는 시스템에서 다형성을 점차 줄이는 또 다른 솔루션입니다.

사용 사례

이중 디스패치는 계산 선택이 해당 인수의 런타임 유형에 따라 달라지는 경우에 유용합니다.예를 들어 프로그래머는 다음과 같은 상황에서 이중 디스패치를 사용할 수 있습니다.

  • 오브젝트의 혼합 세트를 정렬하려면 , 알고리즘에서는 오브젝트 리스트를 표준 순서로 정렬할 필요가 있습니다.한 요소가 다른 요소보다 우선하는지 여부를 판단하려면 두 가지 유형 모두에 대한 지식이 필요하며 필드의 일부 하위 집합이 필요할 수 있습니다.
  • 적응형 충돌 알고리즘에서는 일반적으로 서로 다른 개체 간의 충돌을 서로 다른 방식으로 처리해야 합니다.대표적인 것이 우주선과 소행성의 충돌과 우주 [2]공간의 충돌을 다르게 계산하는 게임 환경이다.
  • 겹치는 스프라이트의 교차점을 다른 방식으로 렌더링해야 하는 알고리즘 그리기.
  • 인사관리시스템은 다른 종류의 업무를 다른 직원에게 파견할 수 있습니다.aschedule어카운터로서 입력된 사용자 오브젝트와 엔지니어링으로서 입력된 작업 오브젝트가 주어진 알고리즘은 해당 작업에 대한 해당 사용자의 스케줄링을 거부합니다.
  • 올바른 이벤트 처리 루틴을 호출하기 위해 이벤트 유형과 리셉터 개체 유형을 모두 사용하는 이벤트 처리 시스템.
  • 잠금 및 키 시스템은 다양한 유형의 잠금과 다양한 유형의 키가 있으며 모든 유형의 키가 여러 유형의 잠금을 엽니다.관련된 객체의 유형을 알아야 할 뿐만 아니라 "특정 키가 특정 잠금을 열었는지 확인하는 것과 관련된 특정 키에 대한 정보"의 하위 집합은 잠금 유형에 따라 다릅니다.

흔한 사자성어

위의 예시와 같이 적절한 알고리즘의 선택은 실행 시 콜의 인수 타입에 근거하고 있는 것이 일반적인 관용어입니다.따라서 콜에는 콜의 동적 해결과 관련된 모든 통상적인 추가 퍼포먼스비용이 적용됩니다.보통 단일 메서드디스패치만을 지원하는 언어보다 많은 비용이 발생합니다.예를 들어 C++에서는 동적 함수 호출은 보통 단일 오프셋 계산에 의해 해결됩니다.이것은 컴파일러가 오브젝트의 메서드 테이블에서 함수의 위치를 알고 있기 때문에 오프셋을 정적으로 계산할 수 있기 때문입니다.이중 디스패치를 지원하는 언어에서, 이것은 약간 더 비싸다. 왜냐하면 컴파일러는 런타임에 메서드 테이블에서 메서드의 오프셋을 계산하기 위해 코드를 생성해야 하기 때문이다. 따라서 전체 명령 경로 길이가 (함수에 대한 호출의 총 수보다 작을 가능성이 큰 양만큼, 매우 부호적이지 않을 수 있다.)등)

루비의 예

일반적인 사용 사례는 디스플레이 포트에 화면이나 프린터 또는 아직 존재하지 않는 개체를 표시하는 것입니다.이것은 다른 매체를 다루는 방법에 대한 순진한 실행이다.

학급 직사각형   방어하다 표시_온(항구)     # 오브젝트 클래스에 따라 올바른 코드를 선택합니다.     사례. 항구       언제 디스플레이 포트         DisplayPort에 표시하기 위한 코드 수       언제 프린터 포트         PrinterPort에 표시하기 위한 # 코드       언제 리모트 포트         RemotePort에 표시하기 위한 코드 수     끝.   끝. 끝. 

Oval, Triangle 및 미디어에 자신을 표시하는 기타 오브젝트에 대해서도 같은 내용을 기술해야 합니다.또, 새로운 타입의 포토를 작성하는 경우는, 모두 고쳐 쓸 필요가 있습니다.문제는 display_on 메서드를 객체에 디스패치하기 위한 것과 표시에 적합한 코드(또는 메서드)를 선택하기 위한 것 등 여러 가지 다형성이 존재한다는 것입니다.

한층 더 깔끔하고 유지보수가 용이한 솔루션은 두 번째 디스패치를 실행하는 것입니다.이번에는 매체에 오브젝트를 표시하는 적절한 방법을 선택합니다.

학급 직사각형   방어하다 표시_온(항구)     2차 디스패치 수     항구.display_displays(표시)(자신)   끝. 끝.  학급 타원형   방어하다 표시_온(항구)     2차 디스패치 수     항구.display_displays(표시)(자신)   끝. 끝.  학급 디스플레이 포트   방어하다 display_displays(표시)(물건)     DisplayPort에 사각형을 표시하는 # 코드   끝.   방어하다 display_displays(표시)(물건)     DisplayPort에 타원형을 표시하는 # 코드   끝.   # ... 끝.  학급 프린터 포트   방어하다 display_displays(표시)(물건)     PrinterPort에 사각형을 표시하기 위한 # 코드   끝.   방어하다 display_displays(표시)(물건)     PrinterPort에 타원형을 표시하는 # 코드   끝.   # ... 끝. 

C++에서의 이중 디스패치

언뜻 보기에 더블 디스패치는 기능의 과부하에 의한 자연스러운 결과인 것 같습니다.함수 오버로드에서는 호출된 함수가 인수 유형에 따라 달라질 수 있습니다.그러나 함수 오버로드는 함수의 내부 이름이 인수의 유형을 인코딩하는 "name mangling"을 사용하여 컴파일 시 수행됩니다.예를 들어 함수는foo(int)내부적으로는 __foo_i 라고 불릴 수 있습니다.foo(double)__foo_d 라고 불릴 수 있습니다.따라서 이름 충돌도 가상 테이블 검색도 없습니다.반면 다이내믹 디스패치는 호출 객체의 유형을 기반으로 합니다.즉, 함수 오버로드 대신 가상 함수(오버라이드)를 사용하여 vtable 룩업이 발생합니다.C++로 기술된 게임 내 충돌의 다음 예를 생각해 보겠습니다.

학급 스페이스십 {}; 학급 아폴로 우주선 : 일반의 스페이스십 {};  학급 소행성 { 일반의:   가상 무효 충돌 대상(스페이스십&) {     표준::외치다 << > "아스타로이드가 우주선에 충돌했습니다.\n";   }   가상 무효 충돌 대상(아폴로 우주선&) {     표준::외치다 << > "아스트로이드는 아폴로 우주선에 충돌했습니다.\n";   } };  학급 폭발하는 아스타로이드 : 일반의 소행성 { 일반의:   무효 충돌 대상(스페이스십&) 덮어쓰다 {     표준::외치다 << > "ExplosingAsteroid가 SpaceShip에 충돌했습니다.\n";   }   무효 충돌 대상(아폴로 우주선&) 덮어쓰다 {     표준::외치다 << > "폭발 아스타로이드는 아폴로 우주선에 충돌했습니다.\n";   } }; 

다음과 같은 경우:

소행성 아스타로이드; 스페이스십 스페이스십; 아폴로 우주선 아폴로 우주선; 

그 후 함수 과부하로 인해

아스타로이드.충돌 대상(스페이스십);  아스타로이드.충돌 대상(아폴로 우주선); 

각각 인쇄합니다.Asteroid hit a SpaceShip그리고.Asteroid hit an ApolloSpacecraft(다이나믹 디스패치를 사용하지 않습니다).더 나아가:

폭발하는 아스타로이드 폭발하는 아스타로이드; 폭발하는 아스타로이드.충돌 대상(스페이스십);  폭발하는 아스타로이드.충돌 대상(아폴로 우주선); 

인쇄하다ExplodingAsteroid hit a SpaceShip그리고.ExplodingAsteroid hit an ApolloSpacecraft동적 디스패치를 사용하지 않고 다시 사용할 수 있습니다.

에 대한 참조Asteroid동적 디스패치가 사용되며 다음 코드가 사용됩니다.

소행성& 아스타로이드 레퍼런스 = 폭발하는 아스타로이드; 아스타로이드 레퍼런스.충돌 대상(스페이스십);  아스타로이드 레퍼런스.충돌 대상(아폴로 우주선); 

인쇄하다ExplodingAsteroid hit a SpaceShip그리고.ExplodingAsteroid hit an ApolloSpacecraft, 역시.그러나 다음 코드는 원하는 대로 작동하지 않습니다.

스페이스십& SpaceShipReference = 아폴로 우주선; 아스타로이드.충돌 대상(SpaceShipReference); 아스타로이드 레퍼런스.충돌 대상(SpaceShipReference); 

바람직한 동작은, 이러한 콜을, 다음의 기능에 바인드 하는 것입니다.theApolloSpacecraft인수로써, 그것이 변수의 인스턴스화된 유형이기 때문에, 예상되는 출력은 다음과 같습니다.Asteroid hit an ApolloSpacecraft그리고.ExplodingAsteroid hit an ApolloSpacecraft다만, 실제로는 출력은Asteroid hit a SpaceShip그리고.ExplodingAsteroid hit a SpaceShip문제는 가상 함수가 C++로 동적으로 디스패치되는 동안 함수의 오버로드는 정적으로 진행된다는 것입니다.

위에서 설명한 문제는 방문자 패턴을 사용하여 이중 디스패치를 시뮬레이션함으로써 해결할 수 있습니다.기존 코드가 확장되어 두 코드가 모두SpaceShip그리고.ApolloSpacecraft기능이 주어져 있다

가상 무효 충돌 대상(소행성& 아스타로이드) {   아스타로이드.충돌 대상(*이것.); } 

그 후, 앞의 예는 아직 올바르게 동작하지 않지만, 우주선이 에이전트로 되도록 콜을 재구성하면 원하는 동작을 할 수 있습니다.

스페이스십& SpaceShipReference = 아폴로 우주선; 소행성& 아스타로이드 레퍼런스 = 폭발하는 아스타로이드; SpaceShipReference.충돌 대상(아스타로이드); SpaceShipReference.충돌 대상(아스타로이드 레퍼런스); 

출력됩니다.Asteroid hit an ApolloSpacecraft그리고.ExplodingAsteroid hit an ApolloSpacecraft역시.열쇠는 이다.theSpaceShipReference.CollideWith(theAsteroidReference);는 실행 시 다음 작업을 수행합니다.

  1. theSpaceShipReference참조이므로 C++는 vtable에서 올바른 메서드를 검색합니다.이 경우, 콜을 실시합니다.ApolloSpacecraft::CollideWith(Asteroid&).
  2. 이내에ApolloSpacecraft::CollideWith(Asteroid&),inAsteroid참고 자료이기 때문에inAsteroid.CollideWith(*this)다른 vtable 룩업이 발생합니다.이 경우,inAsteroid에 대한 참조입니다.ExplodingAsteroid그렇게ExplodingAsteroid::CollideWith(ApolloSpacecraft&)호출됩니다.

C#에서의 이중 디스패치

C#에서는 인수를 받아 인스턴스 메서드를 호출할 때 방문자 패턴을 이용하지 않고 복수 디스패치를 할 수 있다.이것은 전통적인 다형성을 사용하면서 동시에 주장을 [3]역동적으로 표현함으로써 이루어집니다.런타임 바인더는 런타임에 적절한 메서드 오버로드를 선택합니다.이 결정에서는 오브젝트인스턴스의 런타임타입(다형성)과 인수의 런타임타입이 고려됩니다.

에펠에서의 이중 디스패치

에펠 프로그래밍 언어는 에이전트 개념을 이중 디스패치 문제와 연관시킬 수 있습니다.다음 예제에서는 에이전트 언어 구성을 이중 디스패치 문제에 적용합니다.

다양한 형태의 SHAPE 및 SHAPE를 그릴 수 있는 SHAPE의 문제 영역을 고려합니다.SHAPE와 SUFRACE는 그 자체로는 "draw"라고 불리는 함수에 대해 알고 있지만 서로 아는 함수는 없습니다.방문자 패턴을 사용하여 두 가지 유형의 객체가 이중 디스패치로 상호 작용하기를 원합니다.

문제는 다형성 SHAPE를 스스로 그리는 다형성 SHAPE를 얻는 것입니다.

산출량

다음 출력 예시는 2개의 SUFRACE 방문자 객체가 다형성 SHAPE 객체 목록에 다형적으로 전달된 결과를 보여 줍니다.방문자 코드 패턴은 SHAPE와 SURFACE를 전체적으로만 인식하고 특정 유형은 인식하지 않습니다.대신 코드는 런타임 다형성과 에이전트의 메커니즘에 의존하여 이 두 개의 지연 클래스와 그 하위 클래스 간에 매우 유연한 공변 관계를 실현합니다.

ETCHASKETH에 빨간색 폴리곤을 그리고 그래피티에 빨간색 폴리곤을 그립니다.벽은 ETCHASKETH에 회색 직사각형을 그리고 그래피티에 회색 직사각형을 그린다.벽은 ETCHASKETH에 녹색 사변형을 그리고 그래피티에 녹색 사변형을 그리고 있습니다.벽은 ETCHASKETH에 파란색 평행사변형을 그리고 그래피티에 파란색 평행사변형을 그린다.벽은 ETCHASKETH에 노란색 폴리곤을 그리고 그래피티에 노란색 폴리곤을 그립니다.벽은 ETCHASKETH에 보라색 직사각형을 그리고 그래피티에 보라색 직사각형을 그린다.

세우다

SHAPE 또는 SURFACE를 검토하기 전에 이중 디스패치의 높은 수준의 분리 사용을 검토해야 합니다.

방문자 패턴

방문자 패턴은 방문자 개체가 데이터 구조 요소(리스트, 트리 등)를 다형적으로 방문함으로써 방문자 대상 구조 내의 다형 요소 개체에 대해 몇 가지 액션(콜 또는 에이전트)을 적용하는 방식으로 작동합니다.

아래 예에서는 다형성 SHAPE 객체의 목록을 작성하고 다형성 SHAPE 객체의 각각을 다형성 SHAPE로 방문하여 SHAPE를 SHAPE에 그리도록 요청합니다.

 만들다    -- 표면에 도형을 인쇄합니다.   현지의    l_module: 어레이드_리스트 [모양.]    l_module: 어레이드_리스트 [표면]   하니    만들다 l_module.만들다 (6)    l_module.확장하다 (만들다 {폴리곤}.색칠을 하다 ("빨간색"))    l_module.확장하다 (만들다 {직사각형}.색칠을 하다 ('회색'))    l_module.확장하다 (만들다 {사각형}.색칠을 하다 ("녹색"))    l_module.확장하다 (만들다 {평행사변형}.색칠을 하다 ('파랑'))    l_module.확장하다 (만들다 {폴리곤}.색칠을 하다 ("노란색"))    l_module.확장하다 (만들다 {직사각형}.색칠을 하다 ("실패"))     만들다 l_module.만들다 (2)    l_module.확장하다 (만들다 {식각}.만들다)    l_module.확장하다 (만들다 {그래피티_}.만들다)     ~건너 l_module ~하듯이 ic_filename(IC_module) 고리     ~건너 l_module ~하듯이 ic_filename(IC_module) 고리      ic_filename(IC_module).아이템.도면_에이전트 (ic_filename(IC_module).아이템.drawing_data_agent)     끝.    끝.   끝. 

먼저 SHAPE 및 SURFACE 객체의 컬렉션을 만듭니다.그런 다음 목록(SHAPE) 중 하나를 반복하여 다른 목록(SURFACE)의 요소를 차례로 방문할 수 있도록 합니다.위의 예제 코드에서는 SUFRACE 개체가 방문하는 SHAPE 개체입니다.

이 코드는 더블 디스패치패턴의 첫 번째 콜(디스패치)인 'drawing_agent'를 통해 간접적으로 {SURFACE}에 다형성 콜을 발신합니다.간접적이고 다형적인 에이전트('drawing_data_agent')를 전달하므로 방문자 코드는 다음 두 가지 사항만 알 수 있습니다.

  • 표면의 묘화제(예: #21라인의 al_surface.drawing_agent)는 무엇입니까?
  • 쉐이프의 도면 데이터 에이전트(예: 21번째 줄의 al_shape.drawing_data_agent)는 무엇입니까?

SUFFERY와 SHAPE는 모두 자체 에이전트를 정의하기 때문에 방문자 코드를 통해 다형적 또는 기타 적절한 호출 방법을 알 필요가 없습니다.이러한 간접 및 디커플링 수준은 C, C++ 및 Java와 같은 다른 공통 언어에서는 시그니처 매칭에 의한 리플렉션 또는 기능 오버로드 중 하나를 통해서만 달성할 수 있습니다.

표면

{SURFACE.draw}에 대한 다형 콜 내에는 에이전트에 대한 콜이 있으며, 이중 디스패치 패턴에서 두 번째 다형 콜 또는 디스패치가 됩니다.

 지연되었다 학급   표면    특징 {없음.} -- 초기화     만들다     --전류를 초기화합니다.    하니     도면_에이전트 := 대리인 그리다    끝.    특징 -- 액세스    도면_에이전트: 절차. [조금도, 태플 [스트링, 스트링]]     -- Current의 그리기 에이전트.    특징 {없음.} -- 실장     그리다 (a_data_agent: 기능. [조금도, 태플, 태플 [이름., 색.: 스트링]])     -- 전류에 'a_shape'를 그립니다.    현지의     l_결과: 태플 [이름., 색.: 스트링]    하니     l_결과 := a_data_agent (무효)     인쇄물 ("그림을 그리다" + l_결과.색. + " " + l_결과.이름. + "에 " + 유형 + %N)    끝.     유형: 스트링     -- Current 이름을 입력합니다.    지연되었다 끝.    끝. 

회선 #19 의 에이전트 인수와 회선 #24 의 콜 인수는 모두 폴리모픽이며 분리되어 있습니다.{SURFACE.draw 기능에서 'a_data_agent'의 기반이 되는 클래스를 알 수 없으므로 에이전트가 분리되었습니다.운영 에이전트가 어떤 클래스에서 파생되었는지 알 수 있는 방법이 없으므로 SHAPE 또는 그 하위 에이전트 중 하나에서 파생될 필요가 없습니다.이는 에펠 에이전트가 다른 언어의 단일 상속, 동적 및 다형 바인딩에 비해 갖는 뚜렷한 이점입니다.

에이전트는 런타임에 동적으로 다형성이 됩니다.이는 오브젝트가 필요한 순간에 동적으로 생성되기 때문입니다.그 시점에서 오브젝트화된 루틴의 버전이 결정되기 때문입니다.에이전트 시그니처의 결과 유형(즉, 2개의 요소를 가진 이름 있는 TUPLE)에 대한 강력한 지식은 유일합니다.단, 이 특정 요건은 (예를 들어 #25라인은 TUPLE의 명명된 요소를 사용하여 SUFRACE의 '그림' 기능을 실현한다)의 요구에 기초하고 있다.이것은 필수가 되어 회피할 수 없었다(아마도 회피할 수 없을 것이다).

마지막으로 'drawing_agent' 기능만 임의의 클라이언트로 내보내는 방법에 유의하십시오.즉, 방문자 패턴 코드(이 클래스의 유일한 클라이언트)는 작업을 수행하기 위해 에이전트에 대해서만 알면 됩니다(예를 들어 방문한 개체에 적용되는 기능으로 에이전트를 사용).

모양.

SHAPE 클래스는 그려지는 것의 기초(예: 도면 데이터)가 있지만, 그럴 필요는 없습니다.이 경우에도 에이전트는 SHAPE와의 공변관계를 가능한 한 분리하기 위해 필요한 간접 및 클래스 불가지론을 제공합니다.

또한 SHAPE는 모든 클라이언트에 대해 완전히 내보낸 기능으로서만 "drawing_data_agent"를 제공한다는 점에 유의하십시오.따라서 생성 이외의 SHAPE와 대화하는 유일한 방법은 "drawing_data_agent"의 기능을 사용하는 것입니다.이것은 임의의 클라이언트가 간접적이고 다형적으로 SHAPE의 도면 데이터를 수집하기 위해 사용합니다.

 지연되었다 학급   모양.    특징 {없음.} -- 초기화     색칠을 하다 (a_color: 맘에 들다 색.)     -- "color"를 "color"로 만듭니다.    하니     색. := a_color     drawing_data_agent := 대리인 도면_데이터    확신해주다     color_set(컬러 세트): 색..same_string (a_color)    끝.   특징 -- 액세스     drawing_data_agent: 기능. [조금도, 태플, 맘에 들다 도면_데이터]     -- 도면용 데이터 에이전트.    특징 {없음.} -- 실장     도면_데이터: 태플 [이름.: 맘에 들다 이름.; 색.: 맘에 들다 색.]     --전류를 그리기 위해 필요한 데이터.    하니     결과 := [이름., 색.]    끝.     이름.: 스트링     -- Current의 오브젝트명.    지연되었다 끝.     색.: 스트링     -- 전류 색상.   끝. 

고전 우주선의 예

전형적인 우주선의 변형은 하나 이상의 우주선 물체가 불량 소행성이나 우주 정거장과 같은 다른 물건들로 가득 찬 우주를 배회하는 것이다.우리가 원하는 것은 우리가 상상하는 우주에서 두 개의 공변하는 물체 사이의 조우(예: 가능한 충돌)를 처리하기 위한 이중 디스패치 방법입니다.다음 예시에서 USS Enterprise 및 USS Excelsior의 출력은 다음과 같습니다.

Starship Enterprise가 A-001에서 A-002로 위치를 변경합니다.우주선 엔터프라이즈, 소행성 '불량 1호'를 피해 회피에 나섰다!Starship Enterprise가 A-002에서 A-003으로 위치를 변경합니다.우주선 엔터프라이즈, 소행성 '로그 2호'를 피해 회피에 나섰다!스타십 엔터프라이즈가 지나가는 스타십 엑셀시오르에게 과학 팀을 전송합니다!Starship Enterprise가 A-003에서 A-004로 위치를 변경합니다.Starship Excelsior가 A-003에서 A-005로 위치를 변경합니다.우주왕복선 엔터프라이즈, 소행성 '로그 3' 회피 조치!Starship Excelsior는 Space Station Deep Space 9 근처에 있으며 도킹할 수 있습니다.Starship Enterprise가 A-004에서 A-005로 위치를 변경합니다.스타십 엔터프라이즈가 지나가는 스타십 엑셀시오르에게 과학 팀을 전송합니다!Starship Enterprise는 Space Station Deep Space 9 근처에 있으며 도킹할 수 있습니다. 

방문자

전형적인 우주선의 예를 든 방문자는 이중 디스패치 메커니즘도 가지고 있다.

만들다   --우주선 물체가 우주를 방문하거나 이동할 수 있도록 합니다.  현지의   l_module: 어레이드_리스트 [스페이스_오브젝트]   l_엔터프라이즈,   l_sysior: 우주선  하니   만들다 l_엔터프라이즈.make_with_name(이름) ("엔터프라이즈", 'A-001')   만들다 l_sysior.make_with_name(이름) ('엑시어', 'A-003')   만들다 l_module.만들다 (0)   l_module.힘. (l_엔터프라이즈)   l_module.힘. (만들다 {소행성}.make_with_name(이름) ('불량 1', "A-002"))   l_module.힘. (만들다 {소행성}.make_with_name(이름) ('불량 2', 'A-003'))   l_module.힘. (l_sysior)   l_module.힘. (만들다 {소행성}.make_with_name(이름) ('불량 3', "A-004"))   l_module.힘. (만들다 {간격 설정}.make_with_name(이름) ("딥 스페이스 9", 'A-005'))   방문하다 (l_엔터프라이즈, l_module)   l_엔터프라이즈.set_position(설정 위치) ("A-002")   방문하다 (l_엔터프라이즈, l_module)   l_엔터프라이즈.set_position(설정 위치) ('A-003')   방문하다 (l_엔터프라이즈, l_module)   l_엔터프라이즈.set_position(설정 위치) ("A-004")   l_sysior.set_position(설정 위치) ('A-005')   방문하다 (l_엔터프라이즈, l_module)   방문하다 (l_sysior, l_module)   l_엔터프라이즈.set_position(설정 위치) ('A-005')   방문하다 (l_엔터프라이즈, l_module)  끝. 특징 {없음.} -- 실장 방문하다 (a_오브젝트: 스페이스_오브젝트; a_discloss.: 어레이드_리스트 [스페이스_오브젝트])   -- 「a_object」는 「a_object」를 방문합니다.  하니   ~건너 a_discloss. ~하듯이 ic_filename(IC_module) 고리    확인. 첨부된 {스페이스_오브젝트} ic_filename(IC_module).아이템 ~하듯이 al_syslog_object 그리고나서     a_오브젝트.oute_에이전트.불러 ([al_syslog_object.sensor_data_agent])    끝.   끝.  끝. 

이중 디스패치는 회선 #35에서 볼 수 있습니다.이 회선에서는 2개의 간접 에이전트가 연계하여 서로 완벽한 다형성 결합으로 동작하는2개의 공변수 콜을 제공합니다.'visit' 기능의 'a_object'에는 'encounter_agent'가 있으며, 'al_universe_object'에서 나오는 'sensor_data_agent'의 센서 데이터로 호출됩니다.이 예의 다른 흥미로운 부분은 SPACE_OBJECT 클래스와 그 'encounter' 기능입니다.

방문자 액션

SPACE_OBJECT에서 내보낸 유일한 기능은 조우 에이전트 및 센서 데이터뿐 아니라 새 위치를 설정하는 용량입니다.하나의 물체(우주선)가 우주의 각 물체를 방문함에 따라 센서 데이터가 수집되어 그 조우 에이전트에서 방문 물체에 전달된다.여기서 sensor_data_agent로부터의 센서 데이터(즉, sensor_data_agent 쿼리에 의해 반환되는 sensor_data TUPLE의 데이터 요소 항목)가 현재 객체에 대해 평가되고, 그 평가에 근거해 행동 방침이 취해진다(아래의 SPACE_OBJE의 "encounter" 참조).다른 모든 데이터는 {NONE}(으)로 내보냅니다.이것은 C, C++ 및 Java의 Private 스코프와 비슷합니다.내보내기되지 않은 기능으로 데이터와 루틴은 각 SPACE_OBJECT에 의해 내부적으로만 사용됩니다.마지막으로, '인쇄'에 대한 조우 호출에는 SPACE_OBJECT의 가능한 하위 클래스에 대한 특정 정보가 포함되어 있지 않습니다.상속의 이 수준에서 발견되는 것은 일반적인 SPACE_OBJECT의 속성 및 루틴에서 알 수 있는 것을 완전히 기반으로 하는 일반적인 관계적 측면뿐입니다.우리가 스타 우주선, 우주 정거장, 소행성에 대해 알고 있거나 상상하고 있는 것에 근거하여 '프린트'의 출력이 인간으로서 이치에 맞는다는 사실은 단지 논리적 계획이나 우연일 뿐이다.SPACE_OBJECT에는 하위 항목에 대한 특정 지식이 프로그래밍되어 있지 않습니다.

지연되었다 학급 스페이스_오브젝트 특징 {없음.} -- 초기화 make_with_name(이름) (a_name: 맘에 들다 이름.; a_포지션: 맘에 들다 위치)     -- "a_name" 및 "a_position"으로 전류를 초기화합니다.   하니     이름. := a_name     위치 := a_포지션     sensor_data_agent := 대리인 sensor_data     oute_에이전트 := 대리인 맞닥뜨리다   확신해주다     name_set: 이름..same_string (a_name)     position_set(위치 세트): 위치.same_string (a_포지션)   끝. 특징 -- 액세스 oute_에이전트: 절차. [조금도, 태플]     -- Current와의 만남을 관리하는 에이전트. sensor_data_agent: 기능. [조금도, 태플, 첨부된 맘에 들다 sensor_data_module]     -- Current의 센서 데이터를 반환하는 에이전트. 특징 -- 설정 set_position(설정 위치) (a_포지션: 맘에 들다 위치)     -- 「position」에 「a_position」을 설정합니다.   하니     인쇄물 (유형 + " " + 이름. + "에서 위치를 변경하다 + 위치 + " ~로 + a_포지션 + ".%N")     위치 := a_포지션   확신해주다     position_set(위치 세트): 위치.same_string (a_포지션)   끝. 특징 {없음.} -- 실장 맞닥뜨리다 (a_syslog_에이전트: 기능. [조금도, 태플, 첨부된 맘에 들다 sensor_data_module])     -- "a_radar_agent"와의 Current 충돌 상태를 검출하여 보고합니다.   하니     a_syslog_에이전트.불러 ([무효])     확인. 첨부된 {맘에 들다 sensor_data_module} a_syslog_에이전트.last_result(마지막 결과) ~하듯이 al_syslog_data 그리고나서       한다면 것은 아니다. 이름..same_string (al_syslog_data.이름.) 그리고나서         한다면 (위치.same_string (al_syslog_data.위치)) 그리고나서           한다면 ((al_syslog_data.도킹 가능 그리고. 도킹 가능) 그리고.               (is_filename(이것들) 그리고. al_syslog_data.is_filename(이것들)) 그리고.               (관리할 수 있다 그리고. al_syslog_data.관리할 수 없음)) 그리고나서             인쇄물 (유형 + " " + 이름. + "가까워요" + al_syslog_data.유형 + " " +                 al_syslog_data.이름. + 도킹 가능합니다.%N")           그렇지 않으면 ((도킹 가능 그리고. al_syslog_data.도킹 가능) 그리고.                 (is_filename(이것들) 그리고. al_syslog_data.is_filename(이것들)) 그리고.                 (관리할 수 있다 그리고. al_syslog_data.관리할 수 있다)) 그리고나서             인쇄물 (유형 + " " + 이름. + "과학팀을 전송하다" + al_syslog_data.유형 + " " +                 al_syslog_data.이름. + "그들이 지나갈 때!%N")           그렇지 않으면 (is_filename(이것들) 그리고. al_syslog_data.is_not_displays(이것이 아님)) 그리고나서             인쇄물 (유형 + " " + 이름. + 회피하고 회피하는 행동을 취하다 +                 al_syslog_data.유형 + " `" + al_syslog_data.이름. + "! %N")           끝.         끝.       끝.     끝.   끝. 이름.: 스트링     -- 현재 이름. 유형: 스트링     -- 전류 유형.   지연되었다   끝. 위치: 스트링     -- 전류의 위치. 도킹 가능: 부울     --다른 유인물체와 도킹할 수 있습니까?   지연되었다   끝. is_filename(이것들): 부울     -- Current는 유인 물체인가?   지연되었다   끝. 관리할 수 있다: 부울     -- 전류는 이동할 수 있습니까?   지연되었다   끝. sensor_data: 첨부된 맘에 들다 sensor_data_module     -- Current의 센서 데이터.   하니       결과 := [이름., 유형, 위치, 도킹 가능, 것은 아니다. 도킹 가능, is_filename(이것들), 것은 아니다. is_filename(이것들), 관리할 수 있다, 것은 아니다. 관리할 수 있다]     끝.    sensor_data_module: 탈부착 가능한 태플 [이름., 유형, 위치: 스트링; 도킹 가능, 도킹할 수 없음, is_filename(이것들), is_not_displays(이것이 아님), 관리할 수 있다, 관리할 수 없음: 부울]       -- Current의 센서 데이터형 앵커.  끝. 

SPACE_OBJECT에는 다음 세 가지 하위 클래스가 있습니다.

스페이스_오브젝트 소행성 우주선 간격 설정 

이 예에서 소행성 클래스는 '로그' 항목에 사용되고, 두 개의 스타쉽은 SHIPATE, 딥 스페이스 나인은 SPACESTATION에 사용됩니다.각 클래스에서 유일한 전문화는 'type' 기능과 객체의 특정 속성 설정입니다.'name'은 'position'뿐만 아니라 생성 루틴에도 제공됩니다.예: 다음은 SHIPTAX의 예입니다.

학급 우주선 상속하다 스페이스_오브젝트 만들다 make_with_name(이름) 특징 {없음.} -- 실장 유형: 스트링 = '스타쉽'   -- <프리저> 도킹 가능: 부울 = 진실의   -- <프리저> is_filename(이것들): 부울 = 진실의   -- <프리저> 관리할 수 있다: 부울 = 진실의   -- <프리저> 끝. 

우주선의 모든 것은 도킹 가능하고, 유인적이며, 조종이 용이합니다.소행성과 같은 다른 물체들은 이런 것들이 아닙니다.반면, 스페이스테이션은 도킹 가능하고 유인적이지만 기동성이 없습니다.따라서 어떤 객체가 다른 객체와 조우할 때 먼저 위치가 서로 가까운지 확인하고, 만약 그렇다면 객체는 기본 특성에 따라 상호 작용합니다.유형 및 이름이 동일한 개체는 동일한 개체로 간주되므로 상호 작용이 논리적으로 허용되지 않습니다.

에펠의 예시 결론

더블 디스패치와 관련하여, Eiffel은 설계자와 프로그래머가 클래스 루틴을 에이전트로 만들고 직접 객체 피쳐 호출을 하는 대신 이러한 에이전트를 전달함으로써 객체 간 직접적인 지식 수준을 더욱 제거할 수 있도록 합니다.또한 에이전트에는 특정 서명과 가능한 결과(쿼리의 경우)가 있으므로 특정 객체 세부 정보를 포기하지 않고 정적 유형 확인 차량에 이상적입니다.에이전트는 완전히 다형성이므로 결과 코드는 로컬 작업을 수행하는 데 필요한 특정 지식만 가집니다.그렇지 않으면 많은 공변수 객체에 특정 내부 클래스 기능 지식이 분산되어 유지 보수 부담이 가중되지 않습니다.이는 에이전트의 사용과 메커니즘에 의해 보증됩니다.에이전트 사용 시 발생할 수 있는 단점 중 하나는 에이전트가 다이렉트콜 상대보다 계산 비용이 비싸다는 것입니다.따라서 이중 디스패치에서의 에이전트 사용과 방문자 패턴에서의 에이전트의 적용을 추정해서는 안 된다.만약 어떤 사람이 공변수 상호작용에 관여할 클래스 유형의 영역에 관한 설계 한계를 명확하게 볼 수 있다면, 직접 호출이 계산 비용 측면에서 더 효율적인 해결책이다.단, 참가 유형의 클래스 도메인이 증가할 것으로 예상되거나 크게 다를 경우 에이전트는 이중 디스패치 패턴으로 유지보수 부담을 줄이기 위한 뛰어난 솔루션을 제시합니다.

「 」를 참조해 주세요.

레퍼런스

  1. ^ 다중 다형성을 다루기 위한 간단한 기술.OPSLA '86의 Proceedings of Object-orriented Programming Systems, Languages and Applications(오브젝트 지향 프로그래밍 시스템, 언어 및 응용 프로그램, 347-349페이지), 1986년 11월.SIGPLAN 통지, 21 (11)로 인쇄. ISBN0-89791-204-7
  2. ^ Scott Meyers의 보다 효과적인 C++ (Addison-Wesley, 1996년)
  3. ^ "Using Type dynamic (C# Programming Guide)". Microsoft Developer Network. Microsoft. 30 Sep 2009. Retrieved 25 May 2016. ... Overload resolution occurs at run time instead of at compile time if one or more of the arguments in a method call have the type dynamic ...