나는 OCP 원칙 과이를 달성하기 위해 전략 패턴을 사용하는 방법에 대해 읽었습니다 .
저는 이것을 몇 사람에게 설명하려고했지만 제가 생각할 수있는 유일한 예는 “주문”이 어떤 상태인지에 따라 다른 유효성 검사 클래스를 사용하는 것입니다.
온라인에서 몇 가지 기사를 읽었지만 일반적으로 보고서 생성 / 청구서 / 유효성 검사 등과 같은 전략을 사용하는 실제 이유를 설명하지 않습니다.
전략 패턴이 일반적이라고 생각하는 실제 사례가 있습니까?
답변
이것에 대해 :
파일을 암호화해야합니다.
작은 파일의 경우 전체 파일을 읽고 메모리에 보관하는 “메모리 내”전략을 사용할 수 있습니다 (파일 <1GB).
대용량 파일의 경우 파일의 일부를 메모리에서 읽고 부분적으로 암호화 된 결과를 tmp 파일에 저장하는 다른 전략을 사용할 수 있습니다.
이는 동일한 작업에 대한 두 가지 다른 전략 일 수 있습니다.
클라이언트 코드는 다음과 같습니다.
File file = getFile();
Cipher c = CipherFactory.getCipher( file.size() );
c.performAction();
// implementations:
interface Cipher {
public void performAction();
}
class InMemoryCipherStrategy implements Cipher {
public void performAction() {
// load in byte[] ....
}
}
class SwaptToDiskCipher implements Cipher {
public void performAction() {
// swapt partial results to file.
}
}
그만큼
Cipher c = CipherFactory.getCipher( file.size() );
암호에 대한 올바른 전략 인스턴스를 반환합니다.
이게 도움이 되길 바란다.
(나는 Cipher가 올바른 단어인지조차 모르겠습니다 : P)
답변
다시 말하지만, 이전 게시물이지만 여전히 검색에 표시되므로 두 가지 예제를 더 추가하겠습니다 (코드는 C #에 있음). 프로젝트 관리자가 “애플리케이션이 ‘X’를 수행하기를 원하지만 ‘X’는 아직 명확하지 않으며 가까운 장래에 변경 될 수 있습니다. ” 전략 패턴을 설명하는 이 비디오 는 스타 크래프트를 예로 사용합니다.
이 카테고리에 해당하는 항목 :
-
정렬 :이 숫자를 정렬하고 싶지만 BrickSort, BubbleSort 또는 다른 정렬을 사용할지 알 수 없습니다.
-
유효성 검사 : “일부 규칙”에 따라 항목을 확인해야하지만 해당 규칙이 무엇인지 아직 명확하지 않으며 새로운 규칙을 생각할 수 있습니다.
-
게임 : 우리는 플레이어가 움직일 때 걷거나 뛰기를 원하지만 앞으로 수영, 날기, 순간 이동, 지하 굴착 등을 할 수 있어야합니다.
-
정보 저장 : 애플리케이션이 정보를 데이터베이스에 저장하기를 원하지만 나중에 파일을 저장하거나 웹 호출을 할 수 있어야합니다.
-
출력 : X를 일반 문자열로 출력해야하지만 나중에 CSV, XML, JSON 등이 될 수 있습니다.
예
사용자가 데이터베이스의 사람들에게 제품을 할당 할 수있는 프로젝트가 있습니다. 개인에 대한이 제품 지정은 일부 비즈니스 규칙에 따라 “승인 됨”또는 “거부 됨”상태입니다. 예 : 사용자가 특정 연령의 사람에게 제품을 할당하면 해당 상태는 거부되어야합니다. 항목에서 두 필드의 차이가 50보다 크면 상태가 거부됩니다.
이제 개발 단계에서 이러한 비즈니스 규칙은 아직 완전히 명확하지 않으며 언제든지 새로운 규칙이 나올 수 있습니다. stragety-pattern의 힘은 IRule 목록이 제공되는 RuleAgent를 만든 것입니다.
public interface IRule {
bool IsApproved(Assignment assignment);
}
사람에게 제품을 할당하는 순간 RuleAgent를 만들고 규칙 목록 (모두 IRule을 구현 함)을 제공하고 할당을 확인하도록 요청합니다. 그것은 모든 규칙을 통과 할 것입니다. 그들은 모두 동일한 인터페이스를 구현하기 때문에 모두 IsApproved
메소드를 가지고 있으며 그중 하나가 false를 반환하면 false를 반환합니다.
예를 들어 관리자가 갑자기 나타나서 말하면 인턴에 대한 모든 할당 또는 초과 근무에 대한 모든 할당을 거부해야합니다. 다음과 같이 새 수업을 만듭니다.
public OvertimeRule : IRule
{
public bool IsApproved(Assignment assignment) //Interface method
{
if (assignment.Person.Timesheet >= 40)
{
return false;
}
return true;
}
}
public InternRule : IRule
{
public bool IsApproved(Assignment assignment) //Interface method
{
if (assignment.Person.Title == "Intern")
{
return false;
}
return true;
}
}
if 문이나 코드를 계속 추가하거나 제거 할 필요가 없다는 것을 알 수 있습니다. IRUle 인터페이스를 구현하는 새 규칙 클래스를 만들고 필요할 때이를 전환하면됩니다.
또 다른 좋은 예 : Scott Allen의 비디오 시리즈 ( http://www.asp.net/mvc/pluralsight) 에서 애플리케이션의 단위 테스트 부분에서 전략 패턴을 사용합니다.
그는 인기에 따라 항목을 표시하는 페이지가있는 웹 사이트를 구축합니다. 그러나 ‘인기’는 여러 가지 (대부분의 조회수, 대부분의 구독자, 생성 일, 대부분의 활동, 최소 댓글 수 등) 일 수 있으며, 경영진이 아직 주문 방법을 정확히 알지 못하는 경우 다른 방식으로 실험하고 싶을 수 있습니다. 나중에 주문. order 메서드를 사용하여 인터페이스 (IOrderAlgorithm 등)를 만들고 Orderer 개체가 IOrderAlgorithm 인터페이스의 구체적인 구현에 순서를 위임하도록합니다. “CommentOrderer”, “ActivityOrderer”등을 만들 수 있습니다. 그리고 새 요구 사항이 발생하면이를 전환하면됩니다.
답변
주요 사항 :
-
전략 은 행동 디자인 패턴입니다. 알고리즘 계열간에 전환하는 데 사용됩니다.
-
이 패턴에는 하나의 추상 전략 인터페이스 와 해당 인터페이스의 많은 구체적인 전략 구현 ( 알고리즘 )이 포함됩니다.
-
응용 프로그램은 전략 인터페이스 만 사용 합니다. 일부 구성 매개 변수에 따라 구체적인 전략 은 interface에 태그가 지정됩니다 .
Wikipedia의 UML 다이어그램
실제 단어 예 : 항공사에서 몇 달 (7 월 -12 월) 동안 할인을 제공 합니다. 월 수에 따라 가격 옵션을 결정하는 하나의 요금 모듈을 가질 수 있습니다 .
간단한 예를 살펴보십시오. 이 예는 온라인 소매 응용 프로그램으로 확장 할 수 있으며, 특별한 날 / 행복한 시간에 장바구니 항목에 쉽게 할인을 제공합니다.
import java.util.*;
/* Interface for Strategy */
interface OfferStrategy {
public String getName();
public double getDiscountPercentage();
}
/* Concrete implementation of base Strategy */
class NoDiscountStrategy implements OfferStrategy{
public String getName(){
return this.getClass().getName();
}
public double getDiscountPercentage(){
return 0;
}
}
/* Concrete implementation of base Strategy */
class QuarterDiscountStrategy implements OfferStrategy{
public String getName(){
return this.getClass().getName();
}
public double getDiscountPercentage(){
return 0.25;
}
}
/* Context is optional. But if it is present, it acts as single point of contact
for client.
Multiple uses of Context
1. It can populate data to execute an operation of strategy
2. It can take independent decision on Strategy creation.
3. In absence of Context, client should be aware of concrete strategies. Context acts a wrapper and hides internals
4. Code re-factoring will become easy
*/
class StrategyContext {
double price; // price for some item or air ticket etc.
Map<String,OfferStrategy> strategyContext = new HashMap<String,OfferStrategy>();
StrategyContext(double price){
this.price= price;
strategyContext.put(NoDiscountStrategy.class.getName(),new NoDiscountStrategy());
strategyContext.put(QuarterDiscountStrategy.class.getName(),new QuarterDiscountStrategy());
}
public void applyStrategy(OfferStrategy strategy){
/*
Currently applyStrategy has simple implementation. You can use Context for populating some more information,
which is required to call a particular operation
*/
System.out.println("Price before offer :"+price);
double finalPrice = price - (price*strategy.getDiscountPercentage());
System.out.println("Price after offer:"+finalPrice);
}
public OfferStrategy getStrategy(int monthNo){
/*
In absence of this Context method, client has to import relevant concrete Strategies everywhere.
Context acts as single point of contact for the Client to get relevant Strategy
*/
if ( monthNo < 6 ) {
return strategyContext.get(NoDiscountStrategy.class.getName());
}else{
return strategyContext.get(QuarterDiscountStrategy.class.getName());
}
}
}
public class StrategyDemo{
public static void main(String args[]){
StrategyContext context = new StrategyContext(100);
System.out.println("Enter month number between 1 and 12");
int month = Integer.parseInt(args[0]);
System.out.println("Month ="+month);
OfferStrategy strategy = context.getStrategy(month);
context.applyStrategy(strategy);
}
}
산출:
Enter month number between 1 and 12
Month =1
Price before offer :100.0
Price after offer:100.0
Enter month number between 1 and 12
Month =7
Price before offer :100.0
Price after offer:75.0
유용한 기사 :
dzone의 전략 패턴
소스 메이킹 별 전략 패턴
답변
몇 가지 매우 간단한 예를 생각할 수 있습니다.
- 목록 정렬. 전략은 목록의 두 항목 중 “첫 번째”항목을 결정하는 데 사용되는 비교입니다.
- 런타임에 정렬 알고리즘 자체 (QuickSort, HeapSort 등)를 선택할 수있는 애플리케이션이있을 수 있습니다.
- Log4Net 및 Log4j의 어 펜더, 레이아웃 및 필터
- UI 툴킷의 레이아웃 관리자
-
데이터 압축. 유일한 메서드가 다음과 같은 ICompressor 인터페이스가있을 수 있습니다.
byte [] compress (byte [] 입력);
구체적인 압축 클래스는 RunLengthCompression, DeflateCompression 등과 같은 것일 수 있습니다.
답변
전략 패턴의 일반적인 용도 중 하나는 사용자 정의 정렬 전략 (고차 함수가없는 언어에서)을 정의하는 것입니다. 예를 들어 Java에서 문자열 목록을 길이별로 정렬하고 익명의 내부 클래스 (전략 인터페이스 구현)를 전달합니다.
List<String> names = Arrays.asList("Anne", "Joe", "Harry");
Collections.sort(names, new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
Assert.assertEquals(Arrays.asList("Joe", "Anne", "Harry"), names);
비슷한 방식으로, 전략은 예를 들어 db4o에서 객체 데이터베이스가있는 네이티브 쿼리에 사용할 수 있습니다.
List<Document> set = db.query(new Predicate<Document>() {
public boolean match(Document candidate) {
return candidate.getSource().contains(source);
}
});
답변
매일 엔터프라이즈 디렉토리에 대해 사용자 기반을 동기화하는 응용 프로그램이 있습니다. 사용자는 대학에서의 지위에 따라 자격이 있거나 자격이 없습니다. 매일 프로비저닝 프로그램이 진행되고 자격을 갖추어야하는 사용자가 애플리케이션에 프로비저닝되고 프로비저닝 해제되지 않은 사용자가 프로비저닝되었는지 확인합니다 (실제로는 단계적 성능 저하 알고리즘에 따라 다르지만 요점을 벗어남). 토요일에는 각 사용자의 일부 속성을 동기화하고 적절한 자격이 있는지 확인하는 더 철저한 업데이트를 수행합니다. 월말에 나는 그 달의 사용량에 따라 청구서 처리를합니다.
이 동기화를 수행하기 위해 구성 가능한 전략 패턴을 사용합니다. 메인 프로그램은 기본적으로 요일 (동기화 변경 만 / 모두 동기화)과 학사 일정에 따른 학기 시간에 따라 마스터 전략을 선택합니다. 결제주기가 종료되면 결제 전략으로 구성됩니다. 그런 다음 표준 인터페이스를 통해 선택한 전략을 실행합니다.
이것이 얼마나 흔한 지 모르겠지만 전략 패턴에 딱 맞는 것 같았습니다.
답변
나는 이것이 오래된 질문이라는 것을 알고 있지만 최근에 구현 한 또 다른 흥미로운 예가 있다고 생각합니다.
이것은 문서 전달 시스템에서 사용되는 전략 패턴의 매우 실용적인 예입니다.
많은 문서와 일부 메타 데이터가 포함 된 아카이브를받은 PDF 전달 시스템이 있습니다. 메타 데이터를 기반으로 문서를 넣을 위치를 결정했습니다. 말하자면, 데이터에 따라, 나는에서 문서를 저장할 수 A
, B
또는 C
스토리지 시스템 또는 세 가지의 혼합을.
여러 고객이이 시스템을 사용했으며 오류 발생시 롤백 / 오류 처리 요구 사항이 달랐습니다. 하나는 배달 시스템이 첫 번째 오류에서 중지하고 모든 문서가 이미 저장소에 배달 된 상태로 두지 만 프로세스를 중지하고 다른 것은 배달하지 않기를 원했습니다. ; 다른 하나는에 B
저장할 때 오류가 발생한 경우 롤백하기를 원했지만 C
이미 전달 된 것은 그대로 두었습니다 A
. 세 번째 또는 네 번째 것도 다른 요구 사항을 가질 것이라고 상상하기 쉽습니다.
문제를 해결하기 위해 전달 논리와 모든 저장소에서 항목을 롤백하는 방법이 포함 된 기본 전달 클래스를 만들었습니다. 이러한 메서드는 오류 발생시 전달 시스템에서 실제로 호출되지 않습니다. 대신 클래스는 종속성 주입을 사용하여 “롤백 / 오류 처리 전략”클래스 (시스템을 사용하는 고객 기반)를 수신합니다.이 클래스는 오류 발생시 호출되고 해당 전략에 적합한 경우 롤백 메서드를 호출합니다.
전달 클래스 자체는 전략 클래스에 무슨 일이 일어나고 있는지 (어떤 문서가 어떤 스토리지에 전달되었으며 어떤 오류가 발생했는지)보고하고 오류가 발생할 때마다 전략에 계속할지 여부를 묻습니다. 전략에 “중지”라고 표시되면 클래스는 이전에보고 된 정보를 사용하여 전달 클래스에서 호출 할 롤백 메서드를 결정하거나 아무 작업도하지 않는 전략의 “정리”메서드를 호출합니다.
rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);
if (rollbackStrategy.mustAbort()) {
rollbackStrategy.rollback(); // rollback whatever is needed based on reports
return false;
}
지금은 두 개의 서로 다른 전략을 가지고 그래서 하나는이다 QuitterStrategy
다른 하나 (첫 번째 오류 아무것도 정리에 종료하는이)입니다 MaximizeDeliveryToAStrategy
하지 프로세스 중단하고 가능한 한 많이 시도하는 (결코 저장에 전달 롤백 물건 A
,하지만 B
배달이 C
실패하면 물건을 롤백 합니다).
제 이해에서 이것은 전략 패턴의 한 예입니다. 내가 틀렸다고 생각한다면 아래에 댓글을 달고 알려주세요. 전략 패턴의 “순수한”사용을 구성하는 것이 무엇인지, 그리고 내 구현의 어떤 측면이 정의를 위반하는지 궁금합니다. 전략 인터페이스가 약간 뚱뚱하기 때문에 약간 재미있어 보인다고 생각합니다. 지금까지 본 모든 예제는 하나의 방법 만 사용하지만 여전히 알고리즘을 캡슐화한다고 생각합니다 (비즈니스 로직의 일부가 알고리즘으로 간주 될 수 있다면 그렇게 생각합니다).
전략은 전달 실행 중에 이벤트에 대한 알림도 받기 때문에 옵저버 로 간주 될 수도 있습니다. 있지만 이는 또 다른 이야기입니다.
약간의 조사를 통해 이것은 Advisor 라는 “복합 패턴”(예 : MVC, 특정 방식으로 아래에 여러 디자인 패턴을 사용하는 패턴)처럼 보입니다 . 전달을 계속할지 여부에 대한 조언자이지만 요청시 항목을 롤백 할 수 있기 때문에 활성 오류 처리기이기도합니다.
어쨌든 이것은 전략 패턴의 사용이 모두 너무 단순하거나 어리석은 느낌을 줄 수있는 매우 복잡한 예입니다. 다른 패턴과 함께 사용하면 정말 복잡하고 훨씬 더 적용 할 수 있습니다.