유창한 인터페이스

Fluent interface

소프트웨어 엔지니어링에서 유창한 인터페이스는 설계가 메서드 체인에 광범위하게 의존하는 객체 지향 API이다.도메인별 언어(DSL)를 만들어 코드 가독성을 높이는 것이 목표다.이 용어는 에릭 에반스마틴 파울러에 의해 2005년에 만들어졌다.[1]

실행

유창한 인터페이스는 일반적으로 메서드 캐스케이딩을 구현하기 위해 메서드 체인을 사용하여 구현되며(원래적으로 캐스케이딩을 지원하지 않는 언어에서는), 구체적으로는 각 메서드가 부착된 객체를 반환하도록 하는 방식으로 구현된다(흔히 참조).this또는self. 보다 추상적으로 말하면, 유창한 인터페이스는 메서드 체인에서 후속 호출의 지시 컨텍스트를 중계하는데, 일반적으로는 그 컨텍스트가 다음과 같다.

  • 호출된 메서드의 반환 값을 통해 정의됨
  • 자체 참조(새 컨텍스트가 마지막 컨텍스트와 동일함)
  • 보이드 컨텍스트 반환을 통해 종료됨

"불투명한 인터페이스"는 체인을 통한 단계적 방법 이상의 의미를 가지며, "내포된 기능 및 객체 범위 지정"[1]과 같은 다른 기법을 사용하여 DSL처럼 판독되는 인터페이스를 설계하는 것을 수반한다.

역사

이러한 전반적인 인터페이스 스타일은 1970년대 스몰토크에서 메서드 캐스캐이딩의 발명으로 거슬러 올라가지만, 1980년대에는 수많은 사례들이 있었지만, 2005년 말에 "fluent interface"라는 용어가 생겨났다.일반적인 예로는 C++iostream 라이브러리를 들 수 있는데, 이 라이브러리는 C++를 사용한다.<<또는>> 메시지 전달을 위한 연산자, 동일한 개체로 다중 데이터를 전송하고 다른 메서드 호출에 대해 "절연기"를 허용한다.다른 초기 예로는 이 스타일을 객체 생성과 속성 할당에 사용한 가넷 시스템(1988년 Lisp)과 아뮬렛 시스템(1994년 C++)이 있다.

C#

C#LINQ에서 유창한 프로그래밍을 광범위하게 사용하여 "표준 쿼리 연산자"를 사용하여 쿼리를 작성한다.그 시행은 확장 방법에 근거한다.

시합을 하다 번역. = 새로운 사전<끈을 매다, 끈을 매다> {     {"고양이", "챗"},     {"개", "치엔"},     {"물고기", "포아송"},     {"새", "oiseau"} };  // "a"자를 포함한 영어 단어의 번역을 찾으십시오. // 길이별로 정렬하여 대문자로 표시 IE숫자 가능<끈을 매다> 질의하다 = 번역.  .어디에(t => t..포함하다("a"))  .OrderBy(t => t.가치.길이)  .선택(t => t.가치.토우퍼());  // 동일한 쿼리가 점진적으로 생성됨: 시합을 하다 여과된   = 번역..어디에(t => t..포함하다("a")); 시합을 하다 분류된     = 여과된.OrderBy  (t => t.가치.길이); 시합을 하다 파이널쿼리 = 분류된.선택     (t => t.가치.토우퍼()); 

유창한 인터페이스는 동일한 물체를 작동/공유하는 일련의 방법을 연결하는데도 사용될 수 있다.고객 클래스를 만드는 대신 다음과 같이 유창한 인터페이스로 꾸밀 수 있는 데이터 컨텍스트를 만들 수 있다.

// 데이터 컨텍스트 정의 계급 컨텍스트 {     공중의 끈을 매다 이름 { 얻다; 세트; }     공중의 끈을 매다  { 얻다; 세트; }     공중의 끈을 매다 섹스 { 얻다; 세트; }     공중의 끈을 매다 주소 { 얻다; 세트; } }  계급 고객 {     사유의 컨텍스트 _beakes = 새로운 컨텍스트(); // 컨텍스트 초기화      // 속성 값 설정     공중의 고객 이름(끈을 매다 이름)     {         _beakes.이름 = 이름;         돌아오다 ;     }      공중의 고객 (끈을 매다 )     {         _beakes. = ;         돌아오다 ;     }      공중의 고객 섹스(끈을 매다 섹스를)     {         _beakes.섹스 = 섹스를;         돌아오다 ;     }      공중의 고객 주소(끈을 매다 주소를 쓰다)     {         _beakes.주소 = 주소를 쓰다;         돌아오다 ;     }      // 데이터를 콘솔로 인쇄     공중의 공허하게 하다 인쇄하다()     {         콘솔.WriteLine($"이름: {_context.FirstName} \nLast name: {_context.LastName} \nSex: {_context.Sex} \nAddress: {_context.주소}");     } }  계급 프로그램 {     정태의 공허하게 하다 메인(끈을 매다[] 아그)     {         // 객체 생성         고객 c1 = 새로운 고객();         // 단일 선으로 데이터를 할당하고 인쇄하는 메소드 체인 사용         c1.이름("비노드").("스리바스타브").섹스("남성").주소("방갈로르").인쇄하다();     } } 

C++

C++에서 유창한 인터페이스의 일반적인 용도는 과부하 연산자를 체인으로 하는 표준 iostream이다.

다음은 C++에서 보다 전통적인 인터페이스 위에 유창한 인터페이스 래퍼를 제공하는 예다.

 // 기본 정의  계급 글루트앱 {  사유의:      인트로 w_, h_, x_, y_, argc_, display_mode_;      마를 뜨다 **argv_;      마를 뜨다 *제목_;  공중의:      글루트앱(인트로 argc, 마를 뜨다** 아그브) {          argc_ = argc;          argv_ = 아그브;      }      공허하게 하다 setDisplayMode(인트로 모드) {          display_mode_ = 모드;      }      인트로 getDisplayMode() {          돌아오다 display_mode_;      }      공허하게 하다 setWindowSize(인트로 w, 인트로 h) {          w_ = w;          h_ = h;      }      공허하게 하다 setWindowPosition(인트로 x, 인트로 y) {          x_ = x;          y_ = y;      }      공허하게 하다 setTitle(경시하다 마를 뜨다 *칭호를 붙이다) {          제목_ = 칭호를 붙이다;      }      공허하게 하다 만들다(){;}  };  // 기본 사용법  인트로 본래의(인트로 argc, 마를 뜨다 **아그브) {      글루트앱 앱.(argc, 아그브);      앱..setDisplayMode(글루트_더블 글루트_RGBA 글루트_알파 글루트_깊이); // 프레임 버퍼 매개 변수 설정      앱..setWindowSize(500, 500); // 창 매개 변수 설정      앱..setWindowPosition(200, 200);      앱..setTitle("내 OpenGL/GLUT 앱");      앱..만들다();  }   // 유창한 포장지  계급 FlureshGlutApp : 사유의 글루트앱 {  공중의:      FlureshGlutApp(인트로 argc, 마를 뜨다 **아그브) : 글루트앱(argc, 아그브) {} // 상위 생성자 상속      FlureshGlutApp &더블버퍼로() {          setDisplayMode(getDisplayMode()   글루트_더블);          돌아오다 *;      }      FlureshGlutApp &WithRGBA() {          setDisplayMode(getDisplayMode()   글루트_RGBA);          돌아오다 *;      }      FlureshGlutApp &알파와 함께() {          setDisplayMode(getDisplayMode()   글루트_알파);          돌아오다 *;      }      FlureshGlutApp &깊이 있게() {          setDisplayMode(getDisplayMode()   글루트_깊이);          돌아오다 *;      }      FlureshGlutApp &건너편에(인트로 w, 인트로 h) {          setWindowSize(w, h);          돌아오다 *;      }      FlureshGlutApp &에서(인트로 x, 인트로 y) {          setWindowPosition(x, y);          돌아오다 *;      }      FlureshGlutApp &이름 지어진(경시하다 마를 뜨다 *칭호를 붙이다) {          setTitle(칭호를 붙이다);          돌아오다 *;      }      // 생성 후 체인을 연결하는 것은 의미가 없으므로 *이것을 반환하지 마십시오.      공허하게 하다 만들다() {          글루트앱::만들다();      }  };  // 유창한 사용법  인트로 본래의(인트로 argc, 마를 뜨다 **아그브) {      FlureshGlutApp(argc, 아그브)          .더블버퍼로().WithRGBA().알파와 함께().깊이 있게()          .에서(200, 200).건너편에(500, 500)          .이름 지어진("내 OpenGL/GLUT 앱")          .만들다();  } 

자바

JOOQ 라이브러리는 SQL을 자바에서 유창한 API로 모델링한다.jMock 시험 프레임워크에서 유창한 시험 기대치의 예는 다음과 같다.[1]

모의의.기대하다(한 번()).방법("m").와 함께( 또는(문자열내용("안녕"),                                           문자열내용("howdy")) ); 
작가 저자 = 작성자.로서("권위자"); 만들다.에서 선택(저자)       .어디에(존재한다(하나 선택()                    .로부터()                    .어디에(.상태.eq(BOOK_상태.판매됨_OUT))                    .그리고(.작성자_ID.eq(저자.아이디)))); 

Fluflu 주석 프로세서는 Java 주석을 이용한 유창한 API를 만들 수 있다.

Java 8 Lambdas 라이브러리를 사용하면 런타임에 표현 트리의 형태로 Java 8 Lambdas를 객체로 나타낼 수 있으므로, 다음과 같은 대신 형식에 안전한 유창한 인터페이스를 만들 수 있다.

고객 오비지 = ... 오비지.재산("이름").eq("존") 

쓸 수 있다:

방법<고객>(고객 -> 고객.getName() == "존") 

또한, 모의 객체 테스트 라이브러리 EasyMock은 이러한 스타일의 인터페이스를 광범위하게 사용하여 표현력 있는 프로그래밍 인터페이스를 제공한다.

컬렉션 모의 채집합 = 이지모크.makeMock(컬렉션.계급); 이지모크     .기대하다(모의 채집합.제거하다(무효의))     .앤던지기(새로운 널포인터예외())     .리스트원스(); 

Java Swing API에서 LayoutManager 인터페이스는 Container 객체가 구성요소 배치를 제어할 수 있는 방법을 정의한다.더 강력한 사람 중 한 명LayoutManager구현은 GridBagLayout 클래스로, 이 클래스에는GridBagConstraints클래스: 레이아웃 제어 수행 방법 지정이 수업의 이용의 대표적인 예는 다음과 같다.

그리드백레이아웃 글래머러스하게 하다 = 새로운 그리드백레이아웃(); JPanel p = 새로운 JPanel(); p.setLayout( 글래머러스하게 하다 );  제이라벨 l = 새로운 제이라벨("이름:"); JTextField nm = 새로운 JTextField(10);  그리드백컨스트레이트 gc = 새로운 그리드백컨스트레이트(); gc.격자 무늬를 새기다 = 0; gc.격자무늬가 있는 = 0; gc.채우다 = 그리드백컨스트레이트.없음; p.덧셈을( l, gc );  gc.격자 무늬를 새기다 = 1; gc.채우다 = 그리드백컨스트레이트.수평; gc.중량의 = 1; p.덧셈을( nm, gc ); 

이것은 많은 코드를 만들고 여기서 정확히 무슨 일이 일어나고 있는지 보는 것을 어렵게 만든다.Packer클래스는 유창한 메커니즘을 제공하므로 대신 다음과 같이 쓰십시오.[2]

JPanel p = 새로운 JPanel(); 패커 pk의 = 새로운 패커( p );  제이라벨 l = 새로운 제이라벨("이름:"); JTextField nm = 새로운 JTextField(10);  pk의.짐을 꾸리다( l ).격자 무늬를 새기다(0).격자무늬가 있는(0); pk의.짐을 꾸리다( nm ).격자 무늬를 새기다(1).격자무늬가 있는(0).채우다(); 

유창한 API가 소프트웨어 작성 방법을 단순화하고 사용자가 API를 훨씬 더 생산적이고 편안하게 사용할 수 있도록 도와주는 API 언어를 만드는 데 도움을 줄 수 있는 곳이 많은데, 이는 메소드의 반환 값이 항상 그 맥락에서 추가 행동을 위한 컨텍스트를 제공하기 때문이다.

자바스크립트

JavaScript 라이브러리의 몇 가지 변종을 사용하는 많은 예들이 있다: jQuery는 아마도 가장 잘 알려져 있을 것이다.일반적으로 "데이터베이스 쿼리"를 구현하기 위해 유창한 빌더를 사용한다. 예를 들어, Dynamite 클라이언트 라이브러리:

// 테이블에서 항목 가져오기 거래처.게티템('사용자-테이블')     .setHashKey('userId', 'userA')     .setRangeKey('칼럼', '@')     .처형하다()     .그때(기능을 하다(자료) {         // data.properties: 결과 객체     }) 

JavaScript에서 이를 수행하는 간단한 방법은 프로토타입 상속과this.

// https://schier.co/blog/2013/11/14/method-chaining-in-javascript.html의 예  계급 새끼 고양이 {   건설업자() {     .이름을 붙이다 = '가필드';     .색을 칠하다 = 'orange';   }    setName(이름을 붙이다) {     .이름을 붙이다 = 이름을 붙이다;     돌아오다 ;   }    세트컬러(색을 칠하다) {     .색을 칠하다 = 색을 칠하다;     돌아오다 ;   }    절약하다() {     위로하다.통나무를 하다(       구원의${.이름을 붙이다}${.색을 칠하다}새끼 고양이     );     돌아오다 ;   } }  // 사용 새로운 새끼 고양이()   .setName('살렘')   .세트컬러('검은색')   .절약하다(); 

스칼라

스칼라는 특징과 기능을 사용하여 메서드 호출과 클래스 믹스인 모두에 대해 유창한 구문을 지원한다.with키워드예를 들면 다음과 같다.

계급  { 반항하다 rgb(): 투플레3[십진법] } 반대하다 블랙 연장하다  { 무효로 하다 반항하다 rgb(): 투플레3[십진법] = ("0", "0", "0"); }  특성 GUI윈도 {   // 유창한 도면을 위해 이 값을 반환하는 렌더링 방법   반항하다 set_pen_color(색을 칠하다: ): .타자를 치다   반항하다 로 옮기다.(양치류: 포지션): .타자를 치다   반항하다 line_to(양치류: 포지션, end_pos: 포지션): .타자를 치다    반항하다 렌더링하다(): .타자를 치다 =  // 아동 구현이 유창하게 사용되도록 아무 것도 그리지 말고 이것만 반환하십시오.    반항하다 top_left(): 포지션   반항하다 bottom_left(): 포지션   반항하다 top_right(): 포지션   반항하다 bottom_right(): 포지션 }  특성 윈도 보더 연장하다 GUI윈도 {   반항하다 렌더링하다(): GUI윈도 = {     잘 하는 군요.렌더링하다()       .로 옮기다.(top_left())       .set_pen_color(블랙)       .line_to(top_right())       .line_to(bottom_right())       .line_to(bottom_left())       .line_to(top_left())    } }  계급 스윙윈도 연장하다 GUI윈도 { ... }  발랄하게 하다 앱윈 = 새로운 스윙윈도() 와 함께 윈도 보더 앱윈.렌더링하다() 

라쿠

라쿠에서는 여러 가지 접근법이 있지만, 가장 간단한 것 중 하나는 속성을 읽기/쓰기로 선언하고 그 속성을 이용하는 것이다.given키워드유형 주석은 선택 사항이지만, 기본 단계적 타이핑은 공공 속성에 직접 쓰는 것을 훨씬 더 안전하게 한다.

등급 직원{급여 레알의*>0;NonEmptyString 해협 곳*~~ /\S/의 부분 집합;#적어도 하나의non-space 캐릭터 rw달러이다 .name NonEmptyString고 있기 때문이다;;rw은 급여달러 .salary하고 있다. 법 요지{반환 qq:to[END]. 이름:달러.name Surname:달러 .surname rwNonEmptyString달러 .surname다 하위 집합.급여: $.salary END }}  $emperte = Saly.new(); $emperte {.name = 'Sally'; .surname = 'Ride'; .surname = 200; # say offtemperte; # name: 샐리 # 성: 승차감 #급여: 200

PHP

PHP에서는 를 사용하여 현재 객체를 반환할 수 있다.$this예를 나타내는 특수 변수그러므로return $this;이 방법을 통해 인스턴스를 반환할 수 있다.아래 예제는 클래스를 정의한다.Employee그리고 이름, 성, 급여를 설정하는 세 가지 방법.각각 의 인스턴스를 반환한다.Employee방법 사슬링을 허용하는 클래스.

계급 직원 {     사유의 끈을 매다 달러명;     사유의 끈을 매다 $surName;      사유의 끈을 매다 달러화;      공중의 기능을 하다 setName(끈을 매다 달러명)     {         $ this->이름을 붙이다 = 달러명;          돌아오다 $ this;     }      공중의 기능을 하다 세트슈르나메(끈을 매다 달러화)     {         $ this->surName = 달러화;          돌아오다 $ this;     }      공중의 기능을 하다 셋살라리(끈을 매다 달러화)     {         $ this->봉급 = 달러화;          돌아오다 $ this;     }      공중의 기능을 하다 __toString()     {         달러화정보 = '이름: ' ' . $ this->이름을 붙이다 . PHP_EOL;         달러화정보 .= 'Surname: ' . $ this->surName . PHP_EOL;         달러화정보 .= '샐러리:.' . $ this->봉급 . PHP_EOL;          돌아오다 달러화정보;     } }  # 급여 100:00으로 직원 계급인 톰 스미스(Tom Smith)의 새로운 인스턴스를 만드십시오. 달러화 = (새로운 직원())                 ->setName('톰')                 ->세트슈르나메('스미스')                 ->셋살라리('100');  # 직원 인스턴스 값 표시: 메아리치다 달러화;  # 디스플레이: # 이름: 톰 # 성:스미스 # 월급 : 100 

파이톤

Python에서, 반환self예시 방법은 유창한 패턴을 구현하는 한 방법이다.

아무리 언어의 창시자인 귀도 판 로섬에 의해 기가 꺾여,[3] 따라서 언피소닉(관용어가 아닌)으로 간주된다.

계급 :     반항하다 __init___(자아의, 칭호를 붙이다: 발을 동동 구르다) -> 없음:         자아의.칭호를 붙이다 = 칭호를 붙이다      반항하다 움푹 패다(자아의, 공간: 인트로):         """"시에는 지정된 수의 공백이 표시된다."""         자아의.칭호를 붙이다 = " " * 공간 + 자아의.칭호를 붙이다         돌아오다 자아의      반항하다 접미사(자아의, 저자: 발을 동동 구르다):         """"시 이름을 시로 바꿔라."""         자아의.칭호를 붙이다 = f"{자아의.칭호를 붙이다}-{저자}"         돌아오다 자아의 
>>>("여행하지 않은 도로").움푹 패다(4).접미사("로버트 프로스트").칭호를 붙이다 '여행하지 않은 길 - 로버트 프로스트' 

스위프트

Swift 3.0 이상 반환 시self기능에서 유창한 패턴을 구현하는 한 가지 방법이다.

계급 사람 {     시합을 하다 이름:  = ""     시합을 하다 성씨:  = ""     시합을 하다 즐겨찾기Quote:  = ""      @폐기 가능한 결과     펑크 세트(이름: ) -> 셀프 {         자아의.이름 = 이름         돌아오다 자아의     }      @폐기 가능한 결과     펑크 세트(성씨: ) -> 셀프 {         자아의.성씨 = 성씨         돌아오다 자아의     }      @폐기 가능한 결과     펑크 세트(즐겨찾기Quote: ) -> 셀프 {         자아의.즐겨찾기Quote = 즐겨찾기Quote         돌아오다 자아의     } } 
하게 하다 사람 = 사람()     .세트(이름: "존")     .세트(성씨: "도")     .세트(즐겨찾기Quote: "거북이 좋아") 

불변성

Copy-on-write 의미론을 이용하는 불변성 유창한 인터페이스를 만드는 것이 가능하다.이 패턴의 변화에서, 내부 특성을 수정하고 동일한 객체에 대한 참조를 반환하는 대신, 개체는 복제된 객체에 대한 특성이 변경되고, 그 객체는 반환된다.

이 접근법의 이점은 인터페이스를 사용하여 특정 지점에서 분기할 수 있는 물체의 구성을 만들 수 있다는 것이다; 두 개 이상의 물체가 일정한 양의 상태를 공유하고 서로 간섭하지 않고 더 멀리 사용될 수 있다.

JavaScript 예제

Copy-on-write 의미론을 사용하면 위의 JavaScript 예제가 다음과 같이 된다.

계급 새끼 고양이 {   건설업자() {     .이름을 붙이다 = '가필드';     .색을 칠하다 = 'orange';   }    setName(이름을 붙이다) {     경시하다 베끼다 = 새로운 새끼 고양이();     베끼다.색을 칠하다 = .색을 칠하다;     베끼다.이름을 붙이다 = 이름을 붙이다;     돌아오다 베끼다;   }    세트컬러(색을 칠하다) {     경시하다 베끼다 = 새로운 새끼 고양이();     베끼다.이름을 붙이다 = .이름을 붙이다;     베끼다.색을 칠하다 = 색을 칠하다;     돌아오다 베끼다;   }    // ... }  // 사용 경시하다 새끼 고양이1 = 새로운 새끼 고양이()   .setName('살렘');  경시하다 새끼 고양이2 = 새끼 고양이1   .세트컬러('검은색');  위로하다.통나무를 하다(새끼 고양이1, 새끼 고양이2); // -> 키튼({이름: '살렘', 색상: '오렌지' }), 키튼({이름: '살렘', 색상: '검은색' }}) 

문제

컴파일 시 오류를 캡처할 수 없음

타이핑된 언어에서, 모든 매개변수를 요구하는 생성자를 사용하는 것은 컴파일 시간에 실패하는 반면, 유창한 접근방식은 현대 컴파일러의 모든 형식 안전성 검사를 생략하고 런타임 오류만 발생시킬 수 있다.그것은 또한 오류 보호를 위한 "실패-패스트" 접근법과 모순된다.

디버깅 및 오류 보고

단일 줄 체인 문장은 디버거가 체인 내에서 중단점을 설정할 수 없기 때문에 디버깅이 더 어려울 수 있다.디버거에서 한 줄 문구를 밟는 것 또한 덜 편리할 수 있다.

자바를 만들다.니오.바이트버퍼.할당하다(10).되감다().한도를 정하다(100); 

특히 같은 수법으로 여러 차례 통화가 이뤄진 경우 어떤 수법 호출이 예외를 초래했는지 명확하지 않을 수 있다는 점도 문제다.사용자가 체인 내에서 중단점을 설정하고 라인별로 코드를 쉽게 통과할 수 있도록 하는 동시에 가독성을 보존하는 여러 줄로 문구를 구분하여 이러한 문제를 극복할 수 있다.

자바를 만들다.니오.바이트버퍼     .할당하다(10)     .되감다()     .한도를 정하다(100); 

그러나 일부 디버거는 예외적인 역추적에서 항상 첫 번째 줄을 표시하지만, 예외적인 역추적에서는 예외적인 역추적에서는 예외적인 역추적에서 항상 첫 번째 줄을 표시한다.

로깅

유창한 통화 체인의 중간에 로그인을 추가하는 것은 문제가 될 수 있다.예: 다음과 같다.

바이트버퍼 완충하다 = 바이트버퍼.할당하다(10).되감다().한도를 정하다(100); 

상태를 기록하려면 다음과 같이 하십시오.buffer의 뒤에rewind()메서드 콜(method call)은 유창한 콜을 끊어야 한다.

바이트버퍼 완충하다 = 바이트버퍼.할당하다(10).되감다(); 통나무를 하다.디버그("다시 감은 후의 첫 번째 바이트는 "이다. + 완충하다.얻다(0)); 완충하다.한도를 정하다(100); 

이는 C#(위의 Java ByteBuffer 예와 동일한 Java Buffer 사용)와 같이 원하는 로깅 기능을 래핑하는 새로운 확장을 정의함으로써 확장 방법을 지원하는 언어에서 작업할 수 있다.

정태의 계급 바이트 버퍼 익스텐션 {     공중의 정태의 바이트버퍼 로그( 바이트버퍼 완충하다, 로그 통나무를 하다, 액션<바이트버퍼> getMessage)     {         끈을 매다 메세지 = getMessage(완충하다);         통나무를 하다.디버그(메세지);         돌아오다 완충하다;     }  }  // 사용: 바이트버퍼     .할당하다(10)     .되감기()     .로그( 통나무를 하다, b => "다시 감은 후의 첫 번째 바이트는 "이다. + b.얻다(0) )     .한계(100); 

서브클래스

강하게 타이핑된 언어(C++, 자바, C# 등)의 하위 클래스는 반환 유형을 변경하기 위해 유창한 인터페이스에 참여하는 슈퍼 클래스의 모든 방법을 재정의해야 하는 경우가 많다.예를 들면 다음과 같다.

계급 A {     공중의 A 이렇게 하다() { ... } } 계급 B 연장하다 A{     공중의 B 이렇게 하다() { 잘 하는 군요.이렇게 하다(); 돌아오다 ; } // 반환 유형을 B로 변경해야 한다.     공중의 B 그렇게 하다() { ... } } ... A a = 새로운 B().그렇게 하다().이렇게 하다(); // A.doThis()를 무시하지 않더라도 작동 가능. B b = 새로운 B().이렇게 하다().그렇게 하다(); // A.doThis()를 오버라이드하지 않으면 실패함. 

F-bound polymorphism을 표현할 수 있는 언어는 이를 이용하여 이러한 어려움을 피할 수 있다.예를 들면 다음과 같다.

추상적 계급 추상적a<T 연장하다 추상적a<T>> {  @SuppressWarnings("unchecked")  공중의 T 이렇게 하다() { ...; 돌아오다 (T); } }  계급 A 연장하다 추상적a<A> {}   계급 B 연장하다 추상적a<B> {  공중의 B 그렇게 하다() { ...; 돌아오다 ; } }  ... B b = 새로운 B().이렇게 하다().그렇게 하다(); // 작동한다! A a = 새로운 A().이렇게 하다();          // 또한 효과가 있다. 

상위 클래스의 인스턴스를 만들 수 있으려면 두 클래스로 분할해야 한다는 점에 유의하십시오.AbstractA그리고A, 컨텐츠가 없는 후자(필요한 경우 생성자만 포함됨).하위 하위 클래스(등)도 원할 경우 접근 방식을 쉽게 확장할 수 있다.

추상적 계급 추상B<T 연장하다 추상B<T>> 연장하다 추상적a<T> {  @SuppressWarnings("unchecked")  공중의 T 그렇게 하다() { ...; 돌아오다 (T); } } 계급 B 연장하다 추상B<B> {}  추상적 계급 추상C<T 연장하다 추상C<T>> 연장하다 추상B<T> {  @SuppressWarnings("unchecked")  공중의 T foo() { ...; 돌아오다 (T); } } 계급 C 연장하다 추상C<C> {} ... C c = 새로운 C().이렇게 하다().그렇게 하다().foo(); // 작동한다! B b = 새로운 B().이렇게 하다().그렇게 하다();       // 여전히 작동한다. 

스칼라(Scala)와 같이 종속적으로 입력된 언어에서 방법은 항상 반환되는 것으로 명시적으로 정의될 수 있다.this따라서 하위 클래스가 유창한 인터페이스를 활용할 수 있도록 한 번만 정의할 수 있다.

계급 A {     반항하다 이렇게 하다(): .타자를 치다 = { ... } // 이것을 반환하고, 항상 이것을 반환한다. } 계급 B 연장하다 A{     // 오버라이드 필요 없음!     반항하다 그렇게 하다(): .타자를 치다 = { ... } } ... 발랄하게 하다 a: A = 새로운 B().그렇게 하다().이렇게 하다(); // 체인은 양방향으로 작용한다. 발랄하게 하다 b: B = 새로운 B().이렇게 하다().그렇게 하다(); // 그리고 두 방법 체인은 모두 B를 낳는다! 

참고 항목

참조

  1. ^ a b c 마틴 파울러 "플루엔트"인터페이스", 2005년 12월 20일
  2. ^ "Interface Pack200.Packer". Oracle. Retrieved 13 November 2019.
  3. ^ Rossum, Guido van (October 17, 2003). "[Python-Dev] sort() return value". Retrieved 2022-02-01.

외부 링크