示例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,既不是,也不是“它取决于”?
答案 0 :(得分:5)
我会说明确的工厂选项(选项B)通常更好。
在你的第一个例子中,你的Foo类实际上是在做两个工作,它是一个工厂,它是一个代理。两个工作,一个班,让我感到不安。
你的第二个选择对客户承担更多的责任:他们需要知道使用工厂,但这是一个广泛使用的习语,我认为这不难理解。
答案 1 :(得分:0)
A的问题是你必须在Foo中实现IFoo的每个方法。如果只有一对夫妇,这不是什么大不了的事,但如果有几个人就会痛苦。如果您正在使用支持工厂方法的语言,例如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}
答案 2 :(得分:0)
当可能存在单个功能集的多个实现时,使用接口。这听起来好像适用于您的特定情况。
就你的例子而言,我肯定会用B滚动,它更容易维护。 A在各个类[和/或方法]中嵌入了太多共同的逻辑[即平台检测]。如果要构建自己的Factory类,请尝试[通过通用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或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不熟悉这些。
答案 3 :(得分:0)
如果你问我B更好 - 因为Foo本身不需要在平台上进行任何切换。为什么这么重要?好吧,因为你可能想要分别测试所有组件 - 在平台A上分别测试'test'IFoo,FooPlatformA和FooPlatformB,如果你坚持选择Foo,你需要在A和B上测试Foo,而不是只有不同的IFoos。无缘无故地使组件更加耦合。
答案 4 :(得分: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,