전략 패턴

Strategy pattern

컴퓨터 프로그래밍에서 전략 패턴(정책 패턴이라고도 함)은 런타임에 알고리즘을 선택할 수 있는 동작 소프트웨어 설계 패턴입니다.단일 알고리즘을 직접 구현하는 대신 코드는 알고리즘 [1]패밀리에서 사용할 런타임 명령을 수신합니다.

전략을 [2]통해 알고리즘을 사용하는 클라이언트와 독립적으로 변경할 수 있습니다.전략은 Gamma [3]등의 영향력 있는 책 Design Patterns에 포함된 패턴 중 하나로, 유연하고 재사용 가능한 객체 지향 소프트웨어를 설계하는 방법을 설명하기 위해 설계 패턴을 사용하는 개념을 대중화했다.어떤 알고리즘을 사용할지에 대한 결정을 런타임까지 미루면 발신 코드를 보다 유연하고 재사용할 수 있습니다.

예를 들어, 착신 데이터에 대해 검증을 수행하는 클래스는 데이터 유형, 데이터 소스, 사용자 선택 또는 기타 식별 요소에 따라 전략 패턴을 사용하여 검증 알고리즘을 선택할 수 있습니다.이러한 요인은 런타임까지 파악되지 않으며 완전히 다른 검증을 수행해야 할 수 있습니다.검증 개체와는 별도로 캡슐화된 검증 알고리즘(전략)은 코드 복제 없이 시스템의 다른 영역(또는 다른 시스템)에 있는 다른 검증 개체에 의해 사용될 수 있습니다.

일반적으로 전략 패턴은 데이터 구조의 일부 코드에 대한 참조를 저장하고 검색합니다.이는 네이티브 함수 포인터, 퍼스트 클래스 함수, 객체 지향 프로그래밍 언어의 클래스 또는 클래스 인스턴스와 같은 메커니즘에 의해 달성되거나 언어 구현의 내부 코드 스토리지에 리플렉션을 통해 액세스할 수 있습니다.

구조.

UML 클래스 및 시퀀스 다이어그램

전략 설계 패턴에 대한 샘플 UML 클래스 및 시퀀스 다이어그램.[4]

위의 UML 클래스 다이어그램에서는Context클래스는 알고리즘을 직접 구현하지 않습니다.대신,Context를 참조합니다.Strategy인터페이스(알고리즘 실행용)strategy.algorithm()), 즉,Context알고리즘의 실장 방법과는 무관합니다.Strategy1그리고.Strategy2클래스가 실장하다Strategy즉, 알고리즘을 실장(규격화)합니다.
UML 시퀀스 다이어그램은 런타임 상호 작용을 보여 줍니다.Context오브젝트는 알고리즘을 다른 것에 위임합니다.Strategy물건들.첫번째,Contextalgorithm()에서Strategy1object : 알고리즘을 실행하여 결과를 반환한다.Context그 후로는Context전략과 콜을 변경하다algorithm()에서Strategy2object : 알고리즘을 실행하여 결과를 반환한다.Context.

클래스 다이어그램

UML의 전략 패턴

[5]

LePUS3의 전략 패턴(레전드)

C#

다음 예는 C#에 있습니다.

일반의 학급 전략 패턴위키 {     일반의 정적인 무효 주된(스트링[] args)     {         // 전략 준비         변화하다 통상적인 전략    = 신규 통상적인 전략();         변화하다 해피 아워 스트래티지 = 신규 Happy Hour Strategy();          변화하다 첫 번째 고객 = 신규 고객 청구서(통상적인 전략);          // 일반 과금         첫 번째 고객.더하다(1.0, 1);          // 해피 아워 시작         첫 번째 고객.전략. = 해피 아워 스트래티지;         첫 번째 고객.더하다(1.0, 2);          // 신규 고객         변화하다 두 번째 고객 = 신규 고객 청구서(해피 아워 스트래티지);         두 번째 고객.더하다(0.8, 1);         // 고객이 지불한다         첫 번째 고객.인쇄();          // 해피 아워 종료         두 번째 고객.전략. = 통상적인 전략;         두 번째 고객.더하다(1.3, 2);         두 번째 고객.더하다(2.5, 1);         두 번째 고객.인쇄();     } }  // Customer Bill은 고객의 청구서와 거의 관련이 없기 때문에 클래스 이름으로 사용됩니다. 학급 고객 청구서 {     사적인 리스트< >이중으로 하다> 음료수;      // 전략 가져오기/설정     일반의 IBILLINGrategy 전략. { 얻다; 세트; }      일반의 고객 청구서(IBILLINGrategy 전략.)     {         이것..음료수 = 신규 목록.< >이중으로 하다> ( ) ;         이것..전략. = 전략.;     }      일반의 무효 더하다(이중으로 하다 가격., 인트 )     {         이것..음료수.더하다(이것..전략..GetActPrice(액트 프라이스)(가격. * ));     }      // 청구서 지불     일반의 무효 인쇄()     {         이중으로 하다  = 0;         앞지르다 (변화하다 음료비  이것..음료수)         {              += 음료비;         }         콘솔.기입선($"합계 기한: {sum}");         이것..음료수.분명한();     } }  인터페이스 IBILLINGrategy {     이중으로 하다 GetActPrice(액트 프라이스)(이중으로 하다 raw 프라이스); }  // 일반 과금 전략(가격 변동 없음) 학급 통상적인 전략 : IBILLINGrategy {     일반의 이중으로 하다 GetActPrice(액트 프라이스)(이중으로 하다 raw 프라이스) => raw 프라이스; }  // 해피 아워 전략 (50% 할인) 학급 Happy Hour Strategy : IBILLINGrategy {     일반의 이중으로 하다 GetActPrice(액트 프라이스)(이중으로 하다 raw 프라이스) => raw 프라이스 * 0.5; } 

파이썬

다음 예시는 위의 C# 예시와 동일하지만 Python에서는 마찬가지입니다.

수입품 abc 부터 타자 치기 수입품 목록.   학급 청구서:     방어하다 __init__(자신, 과금_실행:'과금 전략'):         자신.음료수: 목록.[흘러가다] = []         자신._filen_displays_displays(비활성화) = 과금_실행      @parames(@parames)     방어하다 과금_실행(자신) -> '과금 전략':         돌아가다 자신._filen_displays_displays(비활성화)      @syslog_syslogs.세터     방어하다 과금_실행(자신, 과금_실행: '과금 전략') -> 없음.:         자신._filen_displays_displays(비활성화) = 과금_실행      방어하다 더하다(자신, 가격.: 흘러가다, : 인트) -> 없음.:         자신.음료수.추가하다(자신.과금_실행.취득_act_price(가격. * ))      방어하다 __str__(자신) -> 스트레이트:         돌아가다 스트레이트(f{(자신.음료수)}")  학급 과금 전략(abc.ABC):     @parames(@parames).추상적 방법     방어하다 취득_act_price(자신, raw_price(원가): 흘러가다) -> 흘러가다:         올리다 구현되지 않은 오류   학급 통상적인 전략(과금 전략):     방어하다 취득_act_price(자신, raw_price(원가): 흘러가다) -> 흘러가다:         돌아가다 raw_price(원가)   학급 Happy Hour Strategy(과금 전략):     방어하다 취득_act_price(자신, raw_price(원가): 흘러가다) -> 흘러가다:         돌아가다 raw_price(원가) * 0.5   방어하다 주된() -> 없음.:     normal_displays(표준)_displays(표준 = 통상적인 전략()     happy_hour_filename(행복시간) = Happy Hour Strategy()      고객_1 = 청구서(normal_displays(표준)_displays(표준)     고객_2 = 청구서(normal_displays(표준)_displays(표준)      # 통상의 과금     고객_1.더하다(2.50, 3)     고객_1.더하다(2.50, 2)      # 해피 아워 시작     고객_1.과금_실행 = happy_hour_filename(행복시간)     고객_2.과금_실행 = happy_hour_filename(행복시간)     고객_1.더하다(3.40, 6)     고객_2.더하다(3.10, 2)      # 해피 아워 종료     고객_1.과금_실행 = normal_displays(표준)_displays(표준     고객_2.과금_실행 = normal_displays(표준)_displays(표준     고객_1.더하다(3.10, 6)     고객_2.더하다(3.10, 2)      # 청구서 인쇄     인쇄물(고객_1)     인쇄물(고객_2)  한다면 __name__ == "_메인__":     주된() 

자바

다음은 Java의 를 제시하겠습니다.

수입품 java.displaces를 클릭합니다.어레이 리스트; 수입품 java.displaces를 클릭합니다.목록.; 수입품 java.displaces.function을 참조해 주세요.IntUnary Operator;  열거하다 과금 전략 {      // 일반 과금 전략(가격 변동 없음)     보통의 (a -> a),     // 해피 아워 전략 (50% 할인)     해피 아워 (a -> a/2),     ;      사적인 최종 IntUnary Operator 전략.;      과금 전략(IntUnary Operator 전략.) {         이것..전략. = 전략.;     }          // 부동 소수점 반올림 오류를 방지하려면 센트로 가격을 사용하십시오.     인트 액트 프라이스 취득(인트 raw 프라이스) {         돌아가다 이것..전략..applyAsInt(raw 프라이스);     }  }  학급 고객 청구서 {     사적인 최종 목록.< >정수> 음료수 = 신규 어레이 리스트<< 고객명 >>님();     사적인 과금 전략 전략.;      일반의 고객 청구서(과금 전략 전략.) {         이것..전략. = 전략.;     }      일반의 무효 더하다(인트 가격., 인트 ) {         이것..음료수.더하다(이것..전략..액트 프라이스 취득(가격.*));     }      // 청구서 지불     일반의 무효 인쇄물() {         인트  = 이것..음료수.개울.().맵토인트(v -> v).();         시스템..나가..인쇄('합계 기한: ' + );         이것..음료수.분명한();     }      // 전략 설정     일반의 무효 전략 설정(과금 전략 전략.) {         이것..전략. = 전략.;     } }  일반의 학급 전략 패턴 {     일반의 정적인 무효 주된(스트링[] 논쟁들) {         // 전략 준비         과금 전략 통상적인 전략    = 과금 전략.보통의;         과금 전략 해피 아워 스트래티지 = 과금 전략.해피 아워;          고객 청구서 첫 번째 고객 = 신규 고객 청구서(통상적인 전략);          // 일반 과금         첫 번째 고객.더하다(100, 1);          // 해피 아워 시작         첫 번째 고객.전략 설정(해피 아워 스트래티지);         첫 번째 고객.더하다(100, 2);          // 신규 고객         고객 청구서 두 번째 고객 = 신규 고객 청구서(해피 아워 스트래티지);         두 번째 고객.더하다(80, 1);         // 고객이 지불한다         첫 번째 고객.인쇄물();          // 해피 아워 종료         두 번째 고객.전략 설정(통상적인 전략);         두 번째 고객.더하다(130, 2);         두 번째 고객.더하다(250, 1);         두 번째 고객.인쇄물();     } } 

전략 및 개방/폐쇄 원칙

각 신차 모델에서 가속 및 브레이크 동작을 선언해야 합니다.

전략 패턴에 따르면 클래스의 행동은 유전되지 않아야 합니다.대신 인터페이스를 사용하여 캡슐화해야 합니다.이는 OCP(Open/Closed Principle)와 호환되며, OCP는 클래스가 확장될 때는 열려있어야 하지만 수정될 때는 닫혀야 한다고 제안합니다.

예를 들어 자동차 클래스를 생각해 보십시오.자동차에 사용할 수 있는 두 가지 기능은 브레이크와 가속입니다.가속 및 브레이크 동작은 모델 간에 자주 변경되므로 일반적인 접근 방식은 하위 클래스에서 이러한 동작을 구현하는 것입니다.이 접근 방식에는 중대한 단점이 있습니다. 즉, 각 신차 모델에서 가속 및 브레이크 동작을 선언해야 합니다.이러한 동작을 관리하는 작업은 모델 수가 증가함에 따라 크게 증가하며 모델 간에 코드를 복제해야 합니다.또한 각 모델의 코드를 조사하지 않고서는 각 모델의 동작의 정확한 특성을 판별하기가 쉽지 않습니다.

전략 패턴은 상속 대신 구성을 사용합니다.전략 패턴에서 동작은 개별 인터페이스 및 이러한 인터페이스를 구현하는 특정 클래스로 정의됩니다.이를 통해 동작과 동작을 사용하는 클래스 간의 분리가 개선됩니다.동작은 사용하는 클래스를 중단하지 않고 변경할 수 있습니다.클래스는, 사용하고 있는 특정의 실장을 큰 코드의 변경 없이 변경함으로써, 동작간에 전환할 수 있습니다.동작은 디자인 타임뿐만 아니라 런타임에도 변경할 수 있습니다.예를 들어, 자동차 물체의 브레이크 동작은 다음과 같이 변경할 수 있습니다.BrakeWithABS()로.Brake()를 변경함으로써brakeBehavior멤버 대상:

브레이크 동작 = 신규 브레이크(); 
/* 캡슐화된 알고리즘 패밀리 * 인터페이스와 그 구현 */ 일반의 인터페이스 IBrake 동작 {     일반의 무효 브레이크(); }  일반의 학급 브레이크 사용ABS 용구 IBrake 동작 {     일반의 무효 브레이크() {         시스템..나가..인쇄("ABS가 적용된 브레이크");     } }  일반의 학급 브레이크 용구 IBrake 동작 {     일반의 무효 브레이크() {         시스템..나가..인쇄("단순 브레이크 작동");     } }  /* 위의 알고리즘을 자유롭게 사용할 수 있는 클라이언트*/ 일반의 추상적인 학급  {     사적인 IBrake 동작 브레이크 동작;      일반의 (IBrake 동작 브레이크 동작) {       이것..브레이크 동작 = 브레이크 동작;     }      일반의 무효 applyBrake(브레이크 적용)() {         브레이크 동작.브레이크();     }      일반의 무효 set 브레이크 동작(IBrake 동작 브레이크 타입) {         이것..브레이크 동작 = 브레이크 타입;     } }  /* 클라이언트1은 컨스트럭터에서1개의 알고리즘(브레이크)을 사용합니다*/ 일반의 학급 세단 확장  {     일반의 세단() {         잘 하는 군요(신규 브레이크());     } }  /* 클라이언트2는 컨스트럭터에서 다른 알고리즘(BrakeWithABS)을 사용합니다*/ 일반의 학급 SUV 확장  {     일반의 SUV() {         잘 하는 군요(신규 브레이크 사용ABS());     } }  /* 자동차 사용 예시 */ 일반의 학급 카의 예 {     일반의 정적인 무효 주된(최종 스트링[] 논쟁들) {          세단차 = 신규 세단();         세단차.applyBrake(브레이크 적용)();  // 이렇게 하면 클래스 "Brake"가 호출됩니다.           SUV카 = 신규 SUV();         SUV카.applyBrake(브레이크 적용)();    // 이렇게 하면 "BrakeWithABS" 클래스가 호출됩니다.          // 브레이크 동작을 동적으로 설정합니다.         SUV카.set 브레이크 동작( 신규 브레이크() );         SUV카.applyBrake(브레이크 적용)();    // 이렇게 하면 클래스 "Brake"가 호출됩니다.     } } 

「 」를 참조해 주세요.

레퍼런스

  1. ^ "The Strategy design pattern - Problem, Solution, and Applicability". w3sDesign.com. Retrieved 2017-08-12.
  2. ^ Eric Freeman, 엘리자베스 Freeman, Kathy Sierra 및 Bert Bates, Head First Design Patterns, First Edition, Chapter 1, Page 24, O'Reilly Media, Inc, 2004.ISBN 978-0-596-00712-6
  3. ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. pp. 315ff. ISBN 0-201-63361-2.{{cite book}}: CS1 maint: 여러 이름: 작성자 목록(링크)
  4. ^ "The Strategy design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
  5. ^ http://www.mcdonaldland.info/2007/11/28/40/

외부 링크