2010-06-08 4 views
5

예 A :플랫폼 간 구현을 위해 인터페이스 또는 팩토리 (및 인터페이스)를 사용해야합니까?

// pseudo code 
interface IFoo { 
    void bar(); 
} 

class FooPlatformA : IFoo { 
    void bar() { /* ... */ } 
} 

class FooPlatformB : IFoo { 
    void bar() { /* ... */ } 
} 

class Foo : IFoo { 
    IFoo m_foo; 
    public Foo() { 
     if (detectPlatformA()} { 
      m_foo = new FooPlatformA(); 
     } else { 
      m_foo = new FooPlatformB(); 
     } 
    } 

    // wrapper function - downside is we'd have to create one 
    // of these for each function, which doesn't seem right. 
    void bar() { 
     m_foo.bar(); 
    } 
} 

Main() { 
    Foo foo = new Foo(); 
    foo.bar(); 
} 

예 B : A는, B는, 둘, 또는 "그것은 의존하지"

더 나은 옵션입니다
// pseudo code 
interface IFoo { 
    void bar(); 
} 

class FooPlatformA : IFoo { 
    void bar() { /* ... */ } 
} 

class FooPlatformB : IFoo { 
    void bar() { /* ... */ } 
} 

class FooFactory { 
    IFoo newFoo() { 
     if (detectPlatformA()} { 
      return new FooPlatformA(); 
     } else { 
      return new FooPlatformB(); 
     } 
    } 
} 

Main() { 
    FooFactory factory = new FooFactory(); 
    IFoo foo = factory.newFoo(); 
    foo.bar(); 
} 

, 예?

답변

0

Foo에서 IFoo의 모든 메소드를 구현해야한다는 문제가 있습니다. 단 두 사람 만있는 것이 큰 문제는 아니지만 수십 명이 있다면 고통 스럽습니다. 당신은 컬 등의 팩토리 메소드를 지원하는 언어로 작업하는 경우, 당신은 IFoo의 팩토리 메소드를 넣을 수 있습니다 :

{define-class abstract IFoo 
    {method abstract {bar}:void} 
    {factory {default}:{this-class} 
     {if platformA? then 
      {return {FooPlatformA}} 
     else 
      {return {FooPlatformB}} 
     } 
    } 
} 

{define-class FooPlatformA {inherits IFoo} 
     {method {bar}:void} 
} 

... 

def foo = {IFoo} 
{foo.bar} 
+0

재미있는 대답, 그러나 나는 C#을 사용하고 있습니다 만, 공장은 여전히 ​​참으로 옵션입니다. 이 시나리오에서 공장을 사용하는 데 명확한 단점이 있는지 궁금합니다. –

0

인터페이스는 단일 기능 세트의 여러 구현이 존재할 수있는 가능성이 때 사용되는 . 이는 특정 시나리오에 적용되는 것처럼 들립니다.

귀하의 사례에 비추어 볼 때, 나는 B와 함께 확실히 롤백 할 것이고, 유지하는 것이 더 쉽습니다. A는 개별 클래스 [및/또는 메소드] 내에 너무 많은 공통 논리 [플랫폼 감지]를 포함합니다. 자신 만의 팩토리 클래스를 작성하려면, 인터페이스마다 \ class 메소드와 반대로 (일반 Resolve<IType>() 메소드 또는 무엇을 통해) 일반화하려고하십시오. 예를 들어

,

// i called it a "container" because it "contains" implementations 
// or instantiation methods for requested types - but it *is* a 
// factory. 
public class Container 
{ 
    // "resolves" correct implementation for a requested type. 
    public IType Resolve<IType>() 
    { 
     IType typed = default (IType); 
     if (isPlatformA) 
     { 
      // switch or function map on IType for correct 
      // platform A implementation 
     } 
     else if (isPlatformB) 
     { 
      // switch or function map on IType for correct 
      // platform B implementation 
     } 
     else 
     { 
      // throw NotSupportedException 
     } 
     return typed; 
    } 
} 

그러나, 자신의 공장 패턴을 구현하는 대신, 당신은 MS의 Unity2.0 또는 성 윈저의 CastleWindsorContainer 같은 대체 구현을 조사 할 수 있습니다. 구성 및 소비가 쉽습니다.

이상적으로,

// use an interface to isolate *your* code from actual 
// implementation, which could change depending on your needs, 
// for instance if you "roll your own" or switch between Unity, 
// Castle Windsor, or some other vendor 
public interface IContainer 
{ 
    IType Resolve<IType>(); 
} 

// custom "roll your own" container, similar to above, 
public class Container : IContainer { } 

// delegates to an instance of a Unity container, 
public class UnityContainer : IContainer { } 

// delegates to an instance of a CastleWindsorContainer, 
public class CastleWindsorContainer : IContainer { } 

아, 나는 너무 NinjectStructureMap에게 소리한다고 가정합니다. Unity 나 CastleWindsor와 마찬가지로 익숙하지 않습니다.

0

나 한테 물어 보면 B가 더 나을 것입니다. 푸 자체는 플랫폼에서 전환 할 필요가 없기 때문입니다. 그게 왜 중요한거야? 글쎄, 아마 모든 컴포넌트를 개별적으로 테스트하고 싶을 것입니다. IFoo를 '테스트'하고 FooPlatformA를 플랫폼 A에, FooPlatformB를 플랫폼 B에 별도로 테스트하고 싶을 것입니다. Foo 내부에서 선택을하면 A와 B 모두에서 Foo를 테스트해야합니다. 다른 IFoos 만. 명백한 이유없이 구성 요소를 더 결합시킵니다.

5

귀하의 명시 적 공장 옵션 (옵션 B)이 일반적으로 더 좋습니다.

첫 번째 예에서 Foo 클래스는 실제로 두 가지 작업을 수행합니다. 팩토리이고 프록시입니다. 한 가지 직업 인 두 가지 일은 나를 불안하게 만듭니다.

두 번째 옵션은 고객에게 약간의 책임을 묻습니다. 공장을 사용하려면 알아야하지만 널리 사용되는 관용구이므로 이해하기 어렵지 않습니다.

+0

좋은 답변입니다. 솔직히 말하면, 4 가지 기능이 나를 넘어서 밀었습니다. 우리 프록시/래퍼/팩토리 모델을 멋진 깨끗한 공장으로 다시 구현합니다. 관련 메모에서 CArch 클래스에 대한 시너지 효과에 대한 의견을 전달할 수 있습니까 (예제 A와 비슷한 것을 보여줍니다) : http://synergy2.svn.sourceforge.net/viewvc/synergy2/trunk/lib/arch/CArch. cpp? view = markup –

+0

감사합니다. 저는이 예를 프록시 생성에 집중하는 것으로 봅니다. 클라이언트의 관점에서 보면 단순한 아키텍처 객체가 사용됩니다. 우리는 클라이언트의 삶을 단순하게 만들기 위해 이러한 프록시 메소드를 만드는 노력을 기울이고 있습니다. 팩토리 메서드는 간단합니다. 팩토리 로직을 다양하게 변형해야합니다. 특히 추상 팩토리 패턴의 이점을 얻으려면 공장 코드를 리팩터링하는 것이 좋습니다. 지금 당장, 우리는 고객의 단순성에 큰 비중을두고 있습니다. – djna

0

랩퍼 class Foo : IFoo에서 인터페이스의 각 구성원을 구현하지 않았으므로 팩토리가 더 안전한 솔루션입니다. IFoo 인터페이스를 수정할 때마다 래퍼를 업데이트해야한다고 상상해보십시오. 프로그래밍 할 때 목표에 따라 유지 관리 가능성을 최대한 고려하십시오.

'플랫폼'을 모두 사용할 수 있습니까? 아니면 하나만 사용할 수 있습니까? 플랫폼 간의 유일한 차이점은 논리입니까? 게임 개발자의 관점에서 볼 때 #defines를 사용하여 구현할 것입니다.

class Platform : IPlatform 
{ 
    void Update() 
    { 
#if PLATFORM_A 
     * ... Logic for platform A */ 
#elif PLATFORM_B 
     * ... Logic for platform A */ 
#endif 
    } 
} 

HTH

,

+1

또한 베스트 프랙티스에 관한 주제를 다룬다. 나는 Foo, Bar, FooBar를 사용하는 것이 일반적인 프로그래밍 패러다임이지만, 당신의 의도를 이해하기 위해 정직하게 몇 번이나 읽어야한다는 것을 이해합니다. : | – Dennis

+0

흠, 알 겠어. 나는 이것을 염두에두고 앞으로 나아갈 것이다. –

관련 문제