공장으로서의 기본 수업?


14

주말 동안 코드를 작성하고 있었고 기본 클래스에서 정적 메소드로 팩토리를 작성하려고한다는 것을 알았습니다.

내 질문은 이것이 ac # idomatic 접근법인지 단순히 아는 것입니까?

기본 클래스에는 파생 클래스에 대한 지식이 있다는 사실에서 비롯되지 않을 수도 있습니다.

즉, 동일한 결과를 얻는 더 간단한 방법은 확실하지 않습니다. 다른 모든 공장 ​​클래스는 (최소한) 불필요한 복잡성처럼 보입니다 (?)

다음과 같은 것 :

class Animal
{
  public static Animal CreateAnimal(string name)
  {
     switch(name)
     {
        case "Shark":
          return new SeaAnimal();
          break;
        case "Dog":
          return new LandAnimal();
          break;
        default:
          throw new Exception("unknown animal");
     }
  }
}
class LandAnimal : Animal
{
}

class SeaAnimal : Animal
{
}

당신은 어떻게 공장을 시험 할 것입니까?

이 질문의 코드로, 나는하지 않을 것입니다. 그러나 그 대답은 코딩 스타일에 더 많은 테스트를 통합하는 과정을 안내하는 데 도움이되었습니다.
Aaron Anodide

Animal 클래스에서 Seaworld와 Landworld를 모두 가져 오면 격리 된 환경에서 처리하기가 더 어려워집니다.

답변:


13

별도의 팩토리 클래스의 장점은 단위 테스트에서 조롱 할 수 있다는 것입니다.

그러나 그렇게하지 않거나 다른 방식으로 다형성으로 만들면 클래스 자체의 정적 Factory 메소드가 정상입니다.


고마워요, 당신은 당신이 다른 방법으로 다형성을 말할 때 당신이 말하는 것에 대한 예를들 수 있습니까?
Aaron Anodide

하나의 팩토리 메소드를 다른 팩토리 메소드로 바꾸고 싶다면. 테스트 목적으로 모의하는 것은 가장 일반적인 예 중 하나입니다.
pdr

18

Generics를 사용하여 switch 문을 피하고 다운 스트림 구현을 기본 클래스에서 분리 할 수 ​​있습니다.

 public static T CreateAnimal<T>() where T: new, Animal
 {
    return new T();
 }

용법:

LandAnimal rabbit = Animal.CreateAnimal();  //Type inference should should just figure out the T by the return indicated.

또는

 var rabbit = Animal.CreateAnimal<LandAnimal>(); 


2
@PeterK. Fowler는 Code Smells의 switch 문을 참조하지만 그의 해결책은 교체하지 않고 팩토리 클래스로 추출해야한다는 것입니다. 즉, 개발시 항상 유형을 알고 있다면 제네릭이 더 나은 솔루션입니다. 그러나 (원래의 예가 암시하는 것처럼) 그렇지 않으면 리플렉션을 사용하여 공장 방법의 전환을 피하는 것이 잘못된 경제가 될 수 있다고 주장합니다.
pdr

switch 문과 if-else-if 체인은 동일합니다. 나는 기본 사건을 강제로 처리하는 컴파일 시간 검사 때문에 전자를 사용한다
Aaron Anodide

4
@PeterK, switch 문에서 코드는 한 곳에 있습니다. 본격적인 OO에서 동일한 코드가 여러 개의 개별 클래스에 분산 될 수 있습니다. 많은 스 니펫을 추가하는 복잡성이 스위치 명령문 보다 바람직 하지 않은 회색 영역이 있습니다 .

@Thorbjorn, 나는 그 정확한 생각을 가지고 있지만 결코 간결하게 말로 해 주셔서 감사합니다
Aaron Anodide

5

극단적으로 가져 오면 공장도 일반적 일 수 있습니다.

interface IFactory<K, T> where K : IComparable
{
    T Create(K key);
}

그러면 어떤 유형의 객체 팩토리도 만들 수 있으며, 그 결과 모든 유형의 객체가 생성 될 수 있습니다. 그것이 더 간단한 지 확실하지 않으며, 더 일반적입니다.

작은 공장 구현에 대한 switch 문에 잘못된 것이 없습니다. 많은 객체 또는 가능한 다른 클래스 계층 객체에 들어가면보다 일반적인 접근 방식이 더 적합하다고 생각합니다.


1

나쁜 생각. 첫째, 공개 폐쇄 원칙을 위반합니다. 새로운 동물의 경우 기본 클래스를 다시 엉망으로 만들면 잠재적으로 깰 수 있습니다. 의존성은 잘못된 길을 갈 것입니다.

구성에서 동물을 만들어야하는 경우 이름과 일치하는 유형을 가져 오기 위해 리플렉션을 사용하고 얻은 유형 정보를 사용하여 인스턴스화하는 것이 더 나은 옵션이지만 이와 같은 구성은 괜찮습니다.

그러나 어쨌든 동물 클래스 계층 구조에서 분리 된 전용 팩토리 클래스를 작성하고 기본 유형이 아닌 IAnimal 인터페이스를 리턴해야합니다. 그러면 유용 할 것입니다. 디커플링을 달성했을 것입니다.


0

이 질문은 제게시의 적절합니다. 어제 거의 같은 코드를 작성하고있었습니다. "Animal"을 프로젝트와 관련된 것으로 바꾸십시오. 여기서는 논의를 위해 "Animal"을 사용하겠습니다. '스위치'문 대신 하나의 변수를 특정 고정 값과 비교하는 것 이상이 포함되는 다소 복잡한 일련의 'if'문이있었습니다. 그러나 그것은 세부 사항입니다. 정적 팩토리 메소드는 디자인이 초기의 빠르고 더러운 코드를 리팩토링하여 나온 것처럼 깔끔하게 디자인하는 것처럼 보였다.

파생 클래스에 대한 지식이있는 기본 클래스를 근거로이 디자인을 거부했습니다. LandAnimal 및 SeaAnimal 클래스가 작고 깔끔하며 쉬운 경우 동일한 소스 파일에있을 수 있습니다. 그러나 공식적으로 정의 된 표준을 준수하지 않는 텍스트 파일을 읽는 큰 지저분한 방법이 있습니다. 자체 소스 파일에 LandAnimal 클래스가 필요합니다.

순환 파일 종속성으로 이어집니다. LandAnimal은 Animal에서 파생되지만 Animal은 LandAnimal, SeaAnimal 및 15 개의 다른 클래스가 이미 존재한다는 것을 알아야합니다. 나는 공장 방법을 꺼내서 자체 파일 (내 동물 응용 프로그램이 아닌 내 주요 응용 프로그램)에 넣었습니다. 정적 공장 방법은 귀엽고 영리한 것처럼 보였지만 실제로는 디자인 문제를 해결하지 못했다는 것을 깨달았습니다.

언어가 많이 바뀌기 때문에 이것이 관용적 인 C #과 어떻게 관련이 있는지 잘 모르겠습니다. 일반적으로 언어와 특유의 관용구와 관습을 평소의 작업 이외의 언어로 무시합니다. 의미가 있다면 내 C #이 "pythonic"으로 보일 수 있습니다. 나는 일반적인 명확성을 목표로합니다.

또한, 작고 간단한 클래스의 경우 정적 팩토리 메소드를 사용하여 향후 작업에서 확장 할 가능성이 없는지 알 수 없습니다. 일부 소스 파일에 모두 포함시키는 것이 좋을 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.