사양 패턴
Specification pattern
컴퓨터 프로그래밍에서 사양 패턴은 특정 소프트웨어 설계 패턴으로, 부울 논리를 사용하여 비즈니스 규칙을 서로 연결함으로써 비즈니스 규칙을 재조합할 수 있다.그 패턴은 도메인 중심 설계의 맥락에서 자주 사용된다.
사양 패턴은 다른 비즈니스 규칙과 결합할 수 있는 비즈니스 규칙을 개략적으로 설명한다.이 패턴에서 사업 논리 단위는 추상 집계 복합 사양 클래스에서 그 기능을 계승한다.복합 사양 클래스는 부울 값을 반환하는 IsSemensivedBy라는 한 함수를 가지고 있다.인스턴스화 후, 사양은 다른 사양과 "체인"되어, 새로운 사양은 쉽게 유지될 수 있지만 사용자 정의가 가능한 비즈니스 로직을 만든다.더욱이, 인스턴스화 시 비즈니스 논리는 제어의 방법 호출 또는 역전을 통해 지속성 리포지토리와 같은 다른 계층의 대표가 되기 위해 그 상태가 변경될 수 있다.
고급 비즈니스/도메인 로직의 런타임 구성을 수행한 결과, 사양 패턴은 임시 사용자 검색 기준을 저장소에서 처리할 낮은 수준의 로직으로 변환하기 위한 편리한 툴이다.
명세서는 재사용 가능한 형태의 논리의 캡슐화이므로 철저하게 단위 시험을 하는 것이 매우 간단하며, 이 맥락에서 사용될 때 또한 보잘것없는 객체 패턴의 구현이다.
코드 예제
C#
공중의 접점 ISpeecification { 바가지 긁다 이스세미티드비(이의를 제기하다 후보); ISpeecification 그리고(ISpeecification 타사의); ISpeecification 안드노(ISpeecification 타사의); ISpeecification 아니면(ISpeecification 타사의); ISpeecification 그렇지 않으면.(ISpeecification 타사의); ISpeecification 아니다(); } 공중의 추상적 계급 복합사양 : ISpeecification { 공중의 추상적 바가지 긁다 이스세미티드비(이의를 제기하다 후보); 공중의 ISpeecification 그리고(ISpeecification 타사의) { 돌아오다 새로운 AndSpecification(이, 타사의); } 공중의 ISpeecification 안드노(ISpeecification 타사의) { 돌아오다 새로운 AndNotSpecification(이, 타사의); } 공중의 ISpeecification 아니면(ISpeecification 타사의) { 돌아오다 새로운 OrSpecification(이, 타사의); } 공중의 ISpeecification 그렇지 않으면.(ISpeecification 타사의) { 돌아오다 새로운 OrNotSpecification(이, 타사의); } 공중의 ISpeecification 아니다() { 돌아오다 새로운 NotSpecification(이); } } 공중의 계급 AndSpecification : 복합사양 { 사유의 ISpeecification 레프트 컨디션; 사유의 ISpeecification 오른쪽 조건; 공중의 AndSpecification(ISpeecification 남겨진, ISpeecification 맞다) { 레프트 컨디션 = 남겨진; 오른쪽 조건 = 맞다; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(이의를 제기하다 후보) { 돌아오다 레프트 컨디션.이스세미티드비(후보) && 오른쪽 조건.이스세미티드비(후보); } } 공중의 계급 AndNotSpecification : 복합사양 { 사유의 ISpeecification 레프트 컨디션; 사유의 ISpeecification 오른쪽 조건; 공중의 AndNotSpecification(ISpeecification 남겨진, ISpeecification 맞다) { 레프트 컨디션 = 남겨진; 오른쪽 조건 = 맞다; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(이의를 제기하다 후보) { 돌아오다 레프트 컨디션.이스세미티드비(후보) && 오른쪽 조건.이스세미티드비(후보) != 진실의; } } 공중의 계급 OrSpecification : 복합사양 { 사유의 ISpeecification 레프트 컨디션; 사유의 ISpeecification 오른쪽 조건; 공중의 OrSpecification(ISpeecification 남겨진, ISpeecification 맞다) { 레프트 컨디션 = 남겨진; 오른쪽 조건 = 맞다; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(이의를 제기하다 후보) { 돌아오다 레프트 컨디션.이스세미티드비(후보) 오른쪽 조건.이스세미티드비(후보); } } 공중의 계급 OrNotSpecification : 복합사양 { 사유의 ISpeecification 레프트 컨디션; 사유의 ISpeecification 오른쪽 조건; 공중의 OrNotSpecification(ISpeecification 남겨진, ISpeecification 맞다) { 레프트 컨디션 = 남겨진; 오른쪽 조건 = 맞다; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(이의를 제기하다 후보) { 돌아오다 레프트 컨디션.이스세미티드비(후보) 오른쪽 조건.이스세미티드비(후보) != 진실의; } } 공중의 계급 NotSpecification : 복합사양 { 사유의 ISpeecification 포장된; 공중의 NotSpecification(ISpeecification x) { 포장된 = x; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(이의를 제기하다 후보) { 돌아오다 !포장된.이스세미티드비(후보); } }
C# 6.0(일반 포함)
공중의 접점 ISpeecification<T> { 바가지 긁다 이스세미티드비(T 후보); ISpeecification<T> 그리고(ISpeecification<T> 타사의); ISpeecification<T> 안드노(ISpeecification<T> 타사의); ISpeecification<T> 아니면(ISpeecification<T> 타사의); ISpeecification<T> 그렇지 않으면.(ISpeecification<T> 타사의); ISpeecification<T> 아니다(); } 공중의 추상적 계급 LinqSpecification<T> : 복합사양<T> { 공중의 추상적 표현<펑크<T, 바가지 긁다>> AsExpression(); 공중의 무효로 하다 바가지 긁다 이스세미티드비(T 후보) => AsExpression().컴파일()(후보); } 공중의 추상적 계급 복합사양<T> : ISpeecification<T> { 공중의 추상적 바가지 긁다 이스세미티드비(T 후보); 공중의 ISpeecification<T> 그리고(ISpeecification<T> 타사의) => 새로운 AndSpecification<T>(이, 타사의); 공중의 ISpeecification<T> 안드노(ISpeecification<T> 타사의) => 새로운 AndNotSpecification<T>(이, 타사의); 공중의 ISpeecification<T> 아니면(ISpeecification<T> 타사의) => 새로운 OrSpecification<T>(이, 타사의); 공중의 ISpeecification<T> 그렇지 않으면.(ISpeecification<T> 타사의) => 새로운 OrNotSpecification<T>(이, 타사의); 공중의 ISpeecification<T> 아니다() => 새로운 NotSpecification<T>(이); } 공중의 계급 AndSpecification<T> : 복합사양<T> { ISpeecification<T> 남겨진; ISpeecification<T> 맞다; 공중의 AndSpecification(ISpeecification<T> 남겨진, ISpeecification<T> 맞다) { 이.남겨진 = 남겨진; 이.맞다 = 맞다; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(T 후보) => 남겨진.이스세미티드비(후보) && 맞다.이스세미티드비(후보); } 공중의 계급 AndNotSpecification<T> : 복합사양<T> { ISpeecification<T> 남겨진; ISpeecification<T> 맞다; 공중의 AndNotSpecification(ISpeecification<T> 남겨진, ISpeecification<T> 맞다) { 이.남겨진 = 남겨진; 이.맞다 = 맞다; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(T 후보) => 남겨진.이스세미티드비(후보) && !맞다.이스세미티드비(후보); } 공중의 계급 OrSpecification<T> : 복합사양<T> { ISpeecification<T> 남겨진; ISpeecification<T> 맞다; 공중의 OrSpecification(ISpeecification<T> 남겨진, ISpeecification<T> 맞다) { 이.남겨진 = 남겨진; 이.맞다 = 맞다; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(T 후보) => 남겨진.이스세미티드비(후보) 맞다.이스세미티드비(후보); } 공중의 계급 OrNotSpecification<T> : 복합사양<T> { ISpeecification<T> 남겨진; ISpeecification<T> 맞다; 공중의 OrNotSpecification(ISpeecification<T> 남겨진, ISpeecification<T> 맞다) { 이.남겨진 = 남겨진; 이.맞다 = 맞다; } 공중의 무효로 하다 바가지 긁다 이스세미티드비(T 후보) => 남겨진.이스세미티드비(후보) !맞다.이스세미티드비(후보); } 공중의 계급 NotSpecification<T> : 복합사양<T> { ISpeecification<T> 타사의; 공중의 NotSpecification(ISpeecification<T> 타사의) => 이.타사의 = 타사의; 공중의 무효로 하다 바가지 긁다 이스세미티드비(T 후보) => !타사의.이스세미티드비(후보); }
파이톤
로부터 a b c 수입하다 A B C, 추상적인 방법 로부터 데이타클라스 수입하다 데이터클라스 로부터 타자 치기 수입하다 아무거나 계급 BaseSpecification(A B C): @abstractmethod 반항하다 is_reason_by(자아의, 후보: 아무거나) -> 바가지 긁다: 높이다 구현되지 않음Error() 반항하다 __call__(자아의, 후보: 아무거나) -> 바가지 긁다: 돌아오다 자아의.is_reason_by(후보) 반항하다 __and___(자아의, 타사의: "BaseSpecification") -> "AndSpecification": 돌아오다 AndSpecification(자아의, 타사의) 반항하다 __or___(자아의, 타사의: "BaseSpecification") -> "OrSpecification": 돌아오다 OrSpecification(자아의, 타사의) 반항하다 __neg__(자아의) -> "NotSpecification": 돌아오다 NotSpecification(자아의) @dataclass(얼어붙은=진실의) 계급 AndSpecification(BaseSpecification): 맨 처음의: BaseSpecification 둘째: BaseSpecification 반항하다 is_reason_by(자아의, 후보: 아무거나) -> 바가지 긁다: 돌아오다 자아의.맨 처음의.is_reason_by(후보) 그리고 자아의.둘째.is_reason_by(후보) @dataclass(얼어붙은=진실의) 계급 OrSpecification(BaseSpecification): 맨 처음의: BaseSpecification 둘째: BaseSpecification 반항하다 is_reason_by(자아의, 후보: 아무거나) -> 바가지 긁다: 돌아오다 자아의.맨 처음의.is_reason_by(후보) 또는 자아의.둘째.is_reason_by(후보) @dataclass(얼어붙은=진실의) 계급 NotSpecification(BaseSpecification): 주제: BaseSpecification 반항하다 is_reason_by(자아의, 후보: 아무거나) -> 바가지 긁다: 돌아오다 아닌 자아의.주제.is_reason_by(후보)
C++
템플릿 <계급 T> 계급 ISpeecification { 공중의: 가상의 ~ISpeecification() = 체납; 가상의 바가지 긁다 이스세미티드비(T 후보) 경시하다 = 0; 가상의 ISpeecification<T>* 그리고(경시하다 ISpeecification<T>& 기타) 경시하다 = 0; 가상의 ISpeecification<T>* 안드노(경시하다 ISpeecification<T>& 기타) 경시하다 = 0; 가상의 ISpeecification<T>* 아니면(경시하다 ISpeecification<T>& 기타) 경시하다 = 0; 가상의 ISpeecification<T>* 그렇지 않으면.(경시하다 ISpeecification<T>& 기타) 경시하다 = 0; 가상의 ISpeecification<T>* 아니다() 경시하다 = 0; }; 템플릿 <계급 T> 계급 복합사양 : 공중의 ISpeecification<T> { 공중의: 가상의 바가지 긁다 이스세미티드비(T 후보) 경시하다 무효로 하다 = 0; 가상의 ISpeecification<T>* 그리고(경시하다 ISpeecification<T>& 기타) 경시하다 무효로 하다; 가상의 ISpeecification<T>* 안드노(경시하다 ISpeecification<T>& 기타) 경시하다 무효로 하다; 가상의 ISpeecification<T>* 아니면(경시하다 ISpeecification<T>& 기타) 경시하다 무효로 하다; 가상의 ISpeecification<T>* 그렇지 않으면.(경시하다 ISpeecification<T>& 기타) 경시하다 무효로 하다; 가상의 ISpeecification<T>* 아니다() 경시하다 무효로 하다; }; 템플릿 <계급 T> 계급 AndSpecification 최종의 : 공중의 복합사양<T> { 공중의: 경시하다 ISpeecification<T>& 왼쪽; 경시하다 ISpeecification<T>& 맞다; AndSpecification(경시하다 ISpeecification<T>& InLeft, 경시하다 ISpeecification<T>& 인라이트) : 왼쪽(InLeft), 맞다(인라이트) { } 가상의 바가지 긁다 이스세미티드비(T 후보) 경시하다 무효로 하다 { 돌아오다 왼쪽.이스세미티드비(후보) && 맞다.이스세미티드비(후보); } }; 템플릿 <계급 T> ISpeecification<T>* 복합사양<T>::그리고(경시하다 ISpeecification<T>& 기타) 경시하다 { 돌아오다 새로운 AndSpecification<T>(*이, 기타); } 템플릿 <계급 T> 계급 AndNotSpecification 최종의 : 공중의 복합사양<T> { 공중의: 경시하다 ISpeecification<T>& 왼쪽; 경시하다 ISpeecification<T>& 맞다; AndNotSpecification(경시하다 ISpeecification<T>& InLeft, 경시하다 ISpeecification<T>& 인라이트) : 왼쪽(InLeft), 맞다(인라이트) { } 가상의 바가지 긁다 이스세미티드비(T 후보) 경시하다 무효로 하다 { 돌아오다 왼쪽.이스세미티드비(후보) && !맞다.이스세미티드비(후보); } }; 템플릿 <계급 T> 계급 OrSpecification 최종의 : 공중의 복합사양<T> { 공중의: 경시하다 ISpeecification<T>& 왼쪽; 경시하다 ISpeecification<T>& 맞다; OrSpecification(경시하다 ISpeecification<T>& InLeft, 경시하다 ISpeecification<T>& 인라이트) : 왼쪽(InLeft), 맞다(인라이트) { } 가상의 바가지 긁다 이스세미티드비(T 후보) 경시하다 무효로 하다 { 돌아오다 왼쪽.이스세미티드비(후보) 맞다.이스세미티드비(후보); } }; 템플릿 <계급 T> 계급 OrNotSpecification 최종의 : 공중의 복합사양<T> { 공중의: 경시하다 ISpeecification<T>& 왼쪽; 경시하다 ISpeecification<T>& 맞다; OrNotSpecification(경시하다 ISpeecification<T>& InLeft, 경시하다 ISpeecification<T>& 인라이트) : 왼쪽(InLeft), 맞다(인라이트) { } 가상의 바가지 긁다 이스세미티드비(T 후보) 경시하다 무효로 하다 { 돌아오다 왼쪽.이스세미티드비(후보) !맞다.이스세미티드비(후보); } }; 템플릿 <계급 T> 계급 NotSpecification 최종의 : 공중의 복합사양<T> { 공중의: 경시하다 ISpeecification<T>& 기타; NotSpecification(경시하다 ISpeecification<T>& InOther) : 기타(InOther) { } 가상의 바가지 긁다 이스세미티드비(T 후보) 경시하다 무효로 하다 { 돌아오다 !기타.이스세미티드비(후보); } }; 템플릿 <계급 T> ISpeecification<T>* 복합사양<T>::안드노(경시하다 ISpeecification<T>& 기타) 경시하다 { 돌아오다 새로운 AndNotSpecification<T>(*이, 기타); } 템플릿 <계급 T> ISpeecification<T>* 복합사양<T>::아니면(경시하다 ISpeecification<T>& 기타) 경시하다 { 돌아오다 새로운 OrSpecification<T>(*이, 기타); } 템플릿 <계급 T> ISpeecification<T>* 복합사양<T>::그렇지 않으면.(경시하다 ISpeecification<T>& 기타) 경시하다 { 돌아오다 새로운 OrNotSpecification<T>(*이, 기타); } 템플릿 <계급 T> ISpeecification<T>* 복합사양<T>::아니다() 경시하다 { 돌아오다 새로운 NotSpecification<T>(*이); }
사용 예
다음 예에서는 인보이스를 회수하여, 만약의 경우에는 회수 대행사에 송부하고 있다.
- 기한이 지났고
- 통지가 발송되었고,
- 그들은 이미 수집 대행사에 있지 않다.
이 예는 논리가 어떻게 '체인'되어 있는가에 대한 최종 결과를 보여주기 위한 것이다.
이 사용 예에서는 송장의 만기일이 30일 이상일 때 충족되는 이전에 정의된 LiddenSpecification 클래스, 고객에게 세 가지 통지를 보냈을 때 충족되는 NoticeSentSpecification 클래스, 콜릿에 송장이 이미 전송되었을 때 충족되는 InCollectionSpecification 클래스를 가정한다.티온 기관이 수업의 실행은 여기서 중요하지 않다.
우리는 이 세 가지 사양을 사용하여 송장이 연체되었을 때, 고객에게 통지가 발송되었을 때, 그리고 이미 회수 기관에 있지 않을 때 충족될 SendToCollection이라는 새로운 사양을 작성했다.
시합을 하다 오버듀 = 새로운 OverDueSpecification(); 시합을 하다 공지사항센트 = 새로운 NoticeSentSpecification(); 시합을 하다 InCollection = 새로운 InCollectionSpecification(); // 사양 패턴 논리 체인의 예 시합을 하다 SendToCollection = 오버듀.그리고(공지사항센트).그리고(InCollection.아니다()); 시합을 하다 InvoiceCollection = 서비스.GetInvoice(); 앞을 내다 (시합을 하다 유동인보이스 에 InvoiceCollection) { 만일 (SendToCollection.이스세미티드비(유동인보이스)) { 유동인보이스.SendToCollection(); } }
참조
- Evans, Eric (2004). Domain Driven Design. Addison-Wesley. p. 224.
외부 링크
- Eric Evans 및 Martin Fowler의 사양
- 사양 패턴: Matt Berter의 프라이머
- 사양 패턴: VB를 사용한 4부 소개.네트 바이 리차드 달튼
- Moshe Brevda에 의한 PHP의 규격 패턴
- Happyr에 의한 PHP에서의 Happyr 독트린 명세
- Simon Strandgaard의 Swift에서의 사양 패턴
- Tiago Delgado Pinto에 의한 TypeScript와 JavaScript의 규격 패턴
- Rolf Vraijdenberger의 플래시 동작 설명서 3의 사양 패턴