2013-01-11 4 views
2

다음과 같은 상황이 있습니다.유형 매개 변수가있는 일반 공장

My Factory 클래스는 CreateStrategy 함수의 입력 문자열 인수를 기반으로 적절한 Strategy 객체를 만들어야합니다.

Strategy1, Strategy2 등은 모두 공통의 StrategyBase 클래스에서 파생됩니다. 그러나 각 전략에는 Factory 클래스에 대한 유형 매개 변수 인 다른 유효성 검사 메커니즘이 있습니다. 그러나 StrategyValidators는 일반적인 유형이 아니며 인터페이스가 다릅니다.

따라서 아래 코드에서 StrategyValidator 유형에 공통된 제약 조건을 지정할 수 없습니다.

저는 C#에 익숙하지 않아이 디자인 문제를 해결할 수있는 메커니즘이 있는지 확실하지 않습니다. 다음은 사용 목적

Factory f = new Factory(); 

f.CreateStrategy<WMIValidator>("WMI"); 
f.CreateStrategy<ABCDValidator>("ABCD"); 
이야

public class Factory 
{ 
    //Create the appropriate Concrete Implementation class based on the type 
    public static StrategyBase CreateStrategy<StrategyValidator>(String Type) 
    { 
     StrategyBase EnumImp = null; 

     // WMI based implementation 
     if (Type == "Type1") 
     { 
      s = Strategy1<StrategyValidator>.Instance; 
     } 
     else if (Type = "Type2") 
     { 
      s = Strategy2<StrategyValidator>.Instance; 
     } 
     return s; 
    } 

    private StrategyBase s; 
} 

을 제안하십시오 WMIValidatorABCDValidator는 관련이없는 종류가 있지만 CreateStrategy 기능에 의해 생성 된 실제 클래스 계층 구조 예에 관련된

입력 파라미터로서

유형 'T'를 사용할 수없는 공통베이스 여기서 StrategyBase

를 갖는 문제를 설명하는 샘플 코드

namespace TestCSharp 
{ 
    public interface IStrategy 
    { 
    }; 

    public interface S1 : IStrategy 
    { 
     void f1(); 
     void f2(); 
    }; 

    public class S1Concrete : S1 
    { 
     public void f1() { } 
     public void f2() { } 
    } 

    public interface S2 : IStrategy 
    { 
     void f3(); 
     void f4(); 
    }; 

    public class S2Concrete : S2 
    { 
     public void f3() { } 
     public void f4() { } 
    }; 

    public interface ProductBase 
    { 
    }; 

    class Product1<T> : ProductBase where T : S1 
    { 
    }; 

    class Product2<T> : ProductBase where T : S2 
    { 
    }; 

    public class Factory 
    { 
     public ProductBase Create<T>(String Type) 
     { 
      if (Type == "P1") 
       return new Product1<T>(); 
      else if (Type == "P2") 
       return new Product2<T>(); 
     } 
    }; 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Factory f = new Factory(); 
      ProductBase s = f.Create<S1Concrete>("Type1"); 
     } 
    } 
} 

내가 오류은 'T' 제네릭 유형 또는 메소드 'TestCSharp.Product1'. 복싱 변환 또는 'T'에서 'TestCSharp.S1'로의 유형 매개 변수 변환이 없습니다.

+1

의도가 무엇인지 약간 혼란 스럽습니까? 공장을 어떻게 사용하고 싶은지 몇 가지 코드를 추가 할 수 있습니까? –

+0

다른 Strategy 객체와 관련된 다른 유효성 검사 유형을 전달하고 싶습니다.CreateStrategy를 사용하면 사용자 지정 유효성 검사 클래스 – Chubsdad

+0

으로 적절한 Strategy 개체를 인스턴스화하는 데 도움이됩니다. 전략은 동일한 목표를 달성하기 위해 완전히 다른 메커니즘으로 작동하므로 고유성이 있으며 공통성이 없습니다. 일반적으로 – Chubsdad

답변

1

나는 그것을 당신이을 할 ProductStrategy을하려는 작업에 따라,이 경우 답이 있다고 생각합니다. 당신이하려고하는 것은 논리를 두 가지로 나누는 것입니다. 그런 다음 generics를 사용하여 다시 결합하려고하지만, 알 수 있듯이 작동하지 않습니다.

위와 비슷한 시나리오를 생각해보십시오. 그러나 IStrategy을 구현하는 각 클래스는 부작용 (즉, 문자열 인쇄)하는 대신 두 가지 방법 중 하나를 사용합니다. 허용되는 유형 범위에 공통점이있는 경우 제네릭을 사용합니다. 방금 언급 한 경우 모두 void를 반환하고 매개 변수를 허용하지 않는 메서드가 있습니다. 그래서 우리는 예를 들어, IStrategy에 메서드를 추가 할 수 있습니다

public interface IStrategy 
{ 
    void ExecuteLogic(); 
}; 

public class S1 : IStrategy 
{ 
    public void ExecuteLogic() 
    { 
     OneMethod(); 
    } 

    void OneMethod() 
    { 
     Console.WriteLine("Hello"); 
    } 
}; 

public class S2 : IStrategy 
{ 
    public void ExecuteLogic() 
    { 
     TotallyDifferentMethod(); 
    } 

    void TotallyDifferentMethod() 
    { 
     Console.WriteLine("World"); 
    } 
}; 

이제, 당신은 또한 Strategy1Strategy2 다른 검증 메커니즘이 있다고 말했다. 그러나, 당신이 같은 방법과 맥락에서 (그리고 같은 매개 변수와 변수들) 그들을 사용하는 것처럼 보입니다. 그래서 그것들을 유사하게 만드는 무언가가 있어야합니다. 그래도 우리가 요구하는 방식으로 IStrategy을 정의한 후에는이를 Create<T>의 제약으로 사용할 수 있습니다. 따라서 Factory은 다음과 같이됩니다.

public class Factory 
{ 
    public ProductBase Create<T>(String Type) where T : IStrategy 
    { 
     if (Type == "P1") 
      return new Product1<T>(); 
     else if (Type == "P2") 
      return new Product2<T>(); 
     return null; 
    } 
}; 

하지만 여전히 하나의 사례가 있습니다.S2을 제네릭 유형으로 사용하여 Product1을 호출하거나 Product2S1으로 사용하지 않으려면 왜 제네릭을 사용합니까? 제품을 상대 전략과 쉽게 결합 할 수 있으며 코드를 현저하게 단순화 할 수 있습니다.

내가 뭔가를 놓친 경우 (또는 전체 질문) 코멘트를 남겨두면 내 대답에 적응하려고 노력할 것입니다.

편집은 지금부터 당신이 당신의 예를 재정의 한과 인터페이스로 S1S2을 사용, 난 당신이 무슨 뜻인지 알 수 있습니다. 한 가지 방법은 Factory.Create에 대한 여러 제네릭 형식 및 제약 조건을 정의하는 것입니다. 예 : 당신의 Product 클래스에 의해 받아 들여질 수 S1S2의 공통 조상이 없기 때문에 제대로 명시된대로, 그렇지 않으면 불가능하다

public ProductBase Create<T1, T2>(String Type) where T1 : S1 where T2 : S2 

.

+0

제 경우에는 ExecuteLogic이라는 공통 함수가 있더라도 각 전략 클래스마다 다른 인수를 사용할 수 있습니다. 또한 최신 코드 게시판에서 Product1과 Product2에 대한 제약 조건에 대해 각각 S1과 S2가되고 IStrategy가 아닌지 확인하십시오. – Chubsdad

+0

@Chubsdad이 경우에는 IStrategy에서 ExecuteLogic 메서드를 제거하면됩니다. 비어 있음) 클래스의 장식으로 IStrategy를 계속 사용하여 전략임을 나타냅니다. 이렇게하면 가독성이 향상되고 우리의 계기로 타입 세이프 (?) 할 수 있습니다. –

+0

@Chubsdad 위의 의견 작성자가 말했듯이 'Product1'이 'S1'로, 'Product2'가 'S2'로 제한되면 제네릭을 사용하여 제품 결합에 대한 대안을 제시 할 수 없습니다 그들의 각각의 전략과 함께. 이미 말했듯이, 다른 전략에는 다른 서명이 있기 때문에 인터페이스로 'IStrategy'를 사용하는 번거 로움을 줄일 수 있습니다. 제품에 전체 로직을 넣을 수 있습니다. – Mir

2

나는 당신의 시나리오를 완전히 이해하지 못한다. 그러나 내가 사용하는 팩토리 패턴이 리플렉션을 사용하여 제품을 인스턴스화해야한다고 말할 수있는 한. 이는 소비자에게 주어진 제품 이름과 함께 사용할 수있는 전략 유형에 대한 힌트를주지 않기 때문에 다소 추한 것입니다.

public class Factory 
{ 
    public ProductBase Create<T>(string name) 
    { 
     Type type; 
     switch (name) 
     { 
      case "P1": 
       type = typeof (Product1<>); 
       break; 
      case "P2": 
       type = typeof (Product2<>); 
       break; 
      case "P3": 
       type = typeof (Product3<>); 
       break; 
      default: 
       return null; 
     } 
     type = type.MakeGenericType(typeof (T)); 
     return (ProductBase) Activator.CreateInstance(type); 
    } 
} 
0

유형을 StrategyValidator로 바꾸려면 함수를 변경할 수 있습니다.

public static StrategyBase CreateStrategy<T>(String Type) where T:StrategyValidator 

으로

public static StrategyBase CreateStrategy<StrategyValidator>(String Type) 

에서

는 조건부 검사를 피할 수 없다, 당신이 질문에 대답합니다.

코드를 단순화하려면 dictionary 또는 Dependency Injection을 사용하는 경우 구성에 다른 조합 ("Type1", "Type2"등)을 이동 한 다음 반성 할 수 있습니다.

예.

if (!dict.ContainsKey(key)) 
     throw New InvalidArgumentException(); 

    StrategyBase EnumImp = null; 

    var instance = dict[key].MakeGenericType(typeOf(type)).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public)); //dict is Dictionary<string, Type> 
0

만들기 <> 함수 오버로드를 고려한 적이 있습니까? 지금 VisualStudio를 편리하게 사용할 수는 없지만 상황에 맞는 다음 코드를 사용할 수 있습니까?

namespace ... { 
    // ... other code here... 

    public class Factory { 
     public Product1<T> Create<T>() where T : S1 { 
      return new Product1<T>(); 
     } 
     public Product2<T> Create<T>() where T : S2 { 
      return new Product2<T>(); 
     } 
    } 

    class Program { 
     static void Main(string[] args) { 
      Factory f = new Factory(); 
      ProductBase s = f.Create<S1Concrete>(); 
     } 
    } 
} 

또한 유형 제약 조건을 낮은 수준으로 옮길 수도 있습니다. 다음과 같이 (? 그는 IProductBase 인터페이스에서 상속) 추상 기본 ProductBase 클래스를 작성하는 고려 : 이것은 당신의 두통의 일부를 완화하는 데 도움이 될 수 있습니다

class ProductBase<T> : IProductBase where T : IStrategy { } 

.

관련 문제