Я должен использовать интерфейс или фабрику (и интерфейс) для межплатформенной реализации?

Пример 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:

// 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();
}

Который является более оптимальным вариантом, пример A, B, ни один, или "он зависит"?

5
задан Andrei Fierbinteanu 7 August 2010 в 07:38
поделиться

5 ответов

Проблема с A заключается в том, что вы должны реализовать каждый метод IFoo в Foo. Это не имеет большого значения, если их всего пара, но неприятно, если их десятки. Если вы работаете с языком, который поддерживает фабричные методы, например Curl, то вы можете поместить фабричный метод в 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
ответ дан 14 December 2019 в 19:01
поделиться

Если вы спросите меня, то B намного лучше - поскольку Foo не нужно делать никаких переключений на платформе. Почему это важно? Ну, поскольку вы, вероятно, хотите тестировать все компоненты отдельно - Foo с "тестовым" IFoo, FooPlatformA отдельно на платформе A и FooPlatformB на платформе B. Если вы поместите выбор внутрь Foo, вам нужно будет тестировать Foo на A и B, а не только на разных IFoo. Это делает компоненты более связанными без видимой причины.

0
ответ дан 14 December 2019 в 19:01
поделиться

Фабрика - более чистое решение, поскольку у вас нет реализации каждого члена интерфейса в оболочке класс 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,

0
ответ дан 14 December 2019 в 19:01
поделиться

Я бы сказал, что ваш явный заводской вариант (вариант B) в целом лучше.

В вашем первом примере ваш класс Foo эффективно выполняет две задачи: это фабрика и прокси. Две работы, одно занятие доставляют мне неудобства.

Ваш второй вариант возлагает на клиента немного больше ответственности: он должен знать, чтобы использовать фабрику, но это настолько широко используемая идиома, что я думаю, ее нетрудно понять.

5
ответ дан 14 December 2019 в 19:01
поделиться

Интерфейсы используются, когда возможно, что существует несколько реализаций одного функционального набора. Похоже, это применимо к вашему конкретному сценарию.

Если говорить о ваших примерах, я бы определенно выбрал B, его легче поддерживать. A встраивает слишком много общей логики [например, определение платформы] в отдельные классы [и / или методы]. Если вы хотите создать свой собственный класс Factory, попробуйте обобщить его [с помощью универсального метода Resolve () или чего-то еще], а не метода \ класса для каждого интерфейса.

Например,

// 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;
    }
}

Однако вместо того, чтобы реализовывать свой собственный шаблон Factory, вы можете изучить альтернативные реализации, такие как MS Unity2.0 или Castle Windsor 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 { }

О, предположим, я должен также обратиться к Ninject и StructureMap . Я просто не так знаком с ними, как с Unity или CastleWindsor.

0
ответ дан 14 December 2019 в 19:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: