2011-12-15 3 views
1

제목이 이상하다는 것을 알고 있으므로 기본 설정을 해보겠습니다."중첩 된"/ 결합 된 전략 패턴?

저는 StyleBundle이라는 객체를 가지고 있습니다. StyleBundle의 Duration과 StyleBundle (Unlimited 또는 PerStyle)의 "type"의 두 가지 사항에 따라 StyleBundle의 전체 Price가 결정됩니다. 여기에 StyleBundle을 빠르게 잘라 냈습니다.

public class StyleBundle 
{ 
    public decimal Price {get; set;} 
    public StyleType Type {get; set;} //STyleType is a simple enum, either "Unlimited" or "PerStyle" 
    public Duration Duration {get; set;} 
} 

여기에 재생 시간이 있습니다. 기본적으로, 내가 StyleBundlePricingStrategy라는 전략 공장에 시간을 통과 등 DurationType.OneYear, DurationType.TwoYears, 같은 값이 될 수 DurationType입니다 열거, ... 내 StyleBundle 클래스에서

public class Duration 
{ 
    public Duration(TimeSpan timeSpan, string name, DurationType type) 
    { 
     this.TimeSpan = timeSpan; 
     this.Name = name; 
     this.Type = type; 
    } 

    public TimeSpan TimeSpan { get; set; } 
    public string Name { get; set; } 
    public DurationType Type { get; set; } 
} 

있습니다. 여기에 그 클래스는 다음과 StyleBundle의 가격을 얻을 수

public interface IPricingStrategy 
{ 
    decimal GetPriceFor(StyleBundle aStyleBundle); 
} 

:

public class StyleBundlePricingFactory 
{ 
    public static IPricingStrategy GetPricing(Duration duration) 
    { 
     if (duration.Type == DurationType.OneYear) { return new OneYearPricingStrategy(); } 
     if (duration.Type == DurationType.TwoYear) { return new TwoYearPricingStrategy(); } 
     etc... 
     etc... 
    } 
} 

클래스는 IPricingStrategy 인터페이스를 구현 반환. 각 전략 클래스는 지정된 DurationType에 대해 가격이 검색되는 방법을 캡슐화합니다. 다음은 OneYearPricingStrategy 클래스의 예입니다.

public class OneYearPricingStrategy : IPricingStrategy 
{ 
    public decimal GetPriceFor(StyleBundle aStyleBundle) 
    { 
     if (aStyleBundle.StylePricingType == StylePricingType.PerStyle) 
     { 
      return aStyleBundle.Products.Count() * 2500m; 
     } 
     else 
     { 
      return 50000m; 
     } 
    } 
} 

좋아, 아주 기본적인 전략 설정입니다.

나를 먹고 것은 당신이 "OneYearPricingStrategy"클래스의 코드 줄을 보면 것입니다 :

if (aStyleBundle.StylePricingType == StylePricingType.PerStyle) 

당신은 내가 여전히 전략 클래스의 조건을 사용해야하는 것을 볼 수 있습니다 StyleBundle 유형을 설명합니다. StyleBundle 유형은 가격 계산 방법에 영향을 미칩니다.

저에게 이것은 "OneYearPricingStratety", "TwoYearPricingStrategy"등으로 작성한 각 전략 클래스의 나쁜 디자인입니다. StylePricingType 조건부가 복사되어 모든 전략 클래스에 붙여 넣어집니다.

이것은 좋지 않습니다. b/c 새로운 StylePricingType을 추가해야 할 때 어떻게됩니까? 각 PricingStrategy 클래스로 돌아가서 코드를 업데이트해야하므로 (다른 것들과 함께) 전체 SRP가 창 밖으로 나옵니다 ...

내가 필요한 것은 어떤 유형의 패턴을 구현하는 것입니다. 그러면 두 개의 "전략"(Duration과 StyleBundleType)을 결합하고 규칙을 한 곳에서 살릴 수 있습니다. 코드를 넘어서게하지 마십시오.

하나의 전략을 구현할 때 STrategy 패턴을 쉽게 소화 할 수 있지만이 두 가지를 조합 한 것이므로 지금 작성한 방식을 알고 있으며 좋은 연습은 아니며 그것을 원한다.

어쩌면 잘못된 패턴일까요?

모든 포인터가 감사 할 것입니다.

감사합니다, 마이크

편집 : 다락방의 대답에 반응

이, 내가 처음에 전략 패턴에 도착 방법에 대한 자세한 내용을 제공합니다. 먼저 전략을 구현하기보다는 코드 냄새라고 생각하고 전략 패턴이 도움이 될 수 있다고 판단했습니다.

처음에는 StyleBundle.Price 속성은 다음과 같이하는 데 사용 :

public decimal Price 
{ 
    get 
    { 
     if (this.StylePricingType == StylePricingType.PerStyle) 
     { 
      if (this.Duration.Type == DurationType.ThreeDays) 
      { 
       _price = 1500m; 
      } 
      else if (this.Duration.Type == DurationType.OneYear) 
      { 
       _price = 2500m; 
      } 
      else if (this.Duration.Type == DurationType.TwoYears) 
      { 
       _price = 2000m; 
      } 
      else if (this.Duration.Type == DurationType.ThreeYears) 
      { 
       _price = 1650m; 
      } 
     } 
     else if (this.StylePricingType == StylePricingType.Unlimited) 
     { 
      if (this.Duration.Type == DurationType.ThreeDays) 
      { 
       throw new Exception("You can not have a StyleBundle of type Unlimited for a Duration of three days."); 
      } 
      else if (this.Duration.Type == DurationType.OneYear) 
      { 
       _price = 50000m; 
      } 
      else if (this.Duration.Type == DurationType.TwoYears) 
      { 
       _price = 40000m; 
      } 
      else if (this.Duration.Type == DurationType.ThreeYears) 
      { 
       _price = 33500m; 
      } 
     } 
     else 
     { 
      throw new Exception("Illegal StylePricingType passed to Product."); 
     } 
     return _price; 
    } 
    private set 
    { 
     _price = value; 
    } 
} 

은 내가 다른 시간 유형을 추가 할 수있는 시간이 내가 할 ... StyleBundle에 와서 코드를 변경해야한다고 보았다 저, 그것은 더 나은 해결책을 찾기에 충분한 동기 부여 원리처럼 보였습니다.

자,이 문제에 대한 전략 디자인 패턴의 응용 프로그램과 함께, 내 StyleBundle.Price 속성은 다음과 같습니다

는 _pricingStrategy이 IPricingStrategy하고 결정이다
public decimal Price 
    { 
     get 
     { 
      return _pricingStrategy.GetPriceFor(this); 
     } 
     private set 
     { 
      _price = value; 
     } 
    } 

최대 새에있는 구현의 결정 StyleBundle의 생성자에서 StyleBundlePricingFactory.GetPricing (duration) 클래스를 호출하면됩니다.

답변

3

코드를 작성하기 전에 패턴을 생각하지 마십시오. 그런 다음 리팩터링을 코딩하십시오. 때로는 패턴으로 리팩터링합니다.

전략 패턴은 일반적으로 클래스에 대한 동작 논리를 위임 할 때 예약됩니다. 예를 들어 클래스가 ChessPlayer 인 경우 Grandmaster implements ChessStrategyNovice implements ChessStrategy은 내 ChessPlayer의 행동 전략을 변경하는 좋은 방법이되지만 ChessPlayer의 인터페이스는 변경하지 않아도됩니다.

귀하의 경우, 복잡한 데이터 계산 전략이 아닌 데이터 만 가지고 있으므로 적합한 데이터 구조를 찾을 수 있습니다. Durations X PricingStyles에 이중으로 중첩 된 HashMap이 정상적으로 작동합니다. 예 :

Price calculatePrice(Duration d, PricingStyle s) { 
    return map.get(d).get(s); 
} 

추 신 : 좋은 디자인의 경우 적은 코드를 사용하는 것이 일반적입니다.

+0

가렛, 제 편집을 참조하십시오. 나의 초기 코드는 "냄새 나는"것이었고, 여기서 전략 패턴을 적용하는 것이 나에게 의미가있는 것처럼 보였다. –

+0

알았어, 내 업데이트 된 답변을 참조하십시오. –