devise模式:抽象工厂与工厂方法
注意:问题在post末尾。
我已经阅读了关于抽象工厂VS工厂方法的其他stackoverflow线程。 我了解每种模式的意图。 但是,我不清楚这个定义。
Factory Method定义了一个用于创build对象的接口,但是可以让子类决定实例化哪个接口。 工厂方法让类将实例化推迟到子类。
相比之下,抽象工厂提供了一个接口,用于创build相关或依赖对象的族,而不指定具体的类。
– John Feminella
抽象工厂看起来与工厂方法非常相似。 我画了几个UML类来说明我的观点。
注意:
- 该图来自www.yuml.com,因此它们并不完美。 但是它的免费服务:)。
- 图表可能不完美。 我仍然在学习GoF的devise模式。
工厂方法:
抽象工厂(只有1个成员):
抽象工厂(更多会员):
问题:
- 如果抽象工厂只有一个创build者和一个产品,它仍然是抽象工厂模式吗? (创build家庭的界面)
- 工厂方法的具体创build者可以从一个接口创build吗?还是必须来自一个类? (类将实例化推迟到子类)
- 如果抽象工厂只能有一个创build者和一个产品,那么抽象工厂和工厂方法之间的唯一区别是,前者的创build者是一个接口,而后者的创build者是一个类?
希望这可以帮助。 它描述了各种types的工厂。 我使用Head First Design Patterns作为参考。 我用yuml.me来表示。
静态工厂
是一个具有静态方法的类来生成各种子types的产品。
简单的工厂
是一类可以产生各种子types的产品。 (比静态工厂好,当添加新types时,基本产品类不需要只改变简单工厂类)
工厂方法
包含一种生成与其types相关的一种产品的方法。 (比简单工厂更好,因为types被推迟到一个子类。)
抽象工厂
生成一系列相关的types。 它比工厂方法明显不同,因为它有多种types的方法。 (这很复杂,请参考下图以获得更好的实际示例)。
来自.NET Framework的示例
DbFactoriesProvider是一个简单的工厂,因为它没有子types。 DbFactoryProvider是一个抽象工厂,因为它可以创build各种相关的数据库对象,如连接和命令对象。
这两种模式当然是相关的!
模式之间的差异通常是有意的。
Factory Method的意图是“定义一个创build对象的接口,但让子类决定实例化哪个类。Factory Method让类将实例化延迟到子类。
抽象工厂的目的是“提供一个创build相关或依赖对象族的界面,而不必指定具体的类”。
纯粹基于这些意向性陈述(引自GoF),我认为Factory Method在某种意义上来说是一个“退化”的抽象工厂 。
他们通常倾向于实现不同,因为工厂方法比抽象工厂简单得多。
然而,它们在实施中也是相关的。 正如GoF书所述,
AbstractFactory只声明创build产品的接口。 由ConcreteProduct子类来实际创build它们。 最常见的做法是为每个产品定义一个工厂方法。
这个c2维基对这个主题也有一些有趣的讨论。
看来,OP的(优秀)问题清单已被忽略。 目前的答案只是提供重新定义。 所以我会试图简洁地回答原来的问题。
- 如果抽象工厂只有一个创build者和一个产品,它仍然是抽象工厂模式吗? (创build家庭的界面)
没有 。 一个抽象工厂必须创build多个产品来构build“相关产品系列”。 规范的GoF示例创buildScrollBar()
和Window()
。 优点(和目的)是抽象工厂可以在其多个产品上实施一个共同的主题。
- 工厂方法的具体创build者可以从一个接口创build吗?还是必须来自一个类? (类将实例化推迟到子类)
首先,我们必须指出,当GoF写他们的书时,Java和C#都不存在。 GoF使用术语接口与特定语言引入的接口types无关。 因此,具体创build者可以从任何API创build。 模式中重要的一点是,API使用自己的Factory Method,所以只有一个方法的接口不能是一个Factory Method,也不能是一个Abstract Factory。
- 如果抽象工厂只能有一个创build者和一个产品,那么抽象工厂和工厂方法之间的唯一区别是,前者的创build者是一个接口,而后者的创build者是一个类?
这个问题在上面的答案之后不再有效。 但是,如果您认为抽象工厂和工厂方法之间的唯一区别是创build的产品数量,请考虑客户端如何使用这些模式中的每一个。 抽象工厂通常被注入到客户端,并通过组合/委托来调用。 工厂方法必须被inheritance。 所以这一切都回到了旧的构成与inheritance的辩论。
但是这些答案提出了第四个问题!
- 因为只有一个方法的接口不能是一个Factory方法 ,而只能是一个Abstract Factory ,所以我们只用一个方法称之为一个创build接口?
如果方法是静态的,通常称为静态工厂 。 如果方法是非静态的,通常称为简单工厂 。 这些都不是GoF模式,但实际上它们更常用!
在我看来,两种模式之间的细微差别在于适用性 ,因此,正如已经说过的那样,在意图中 。
让我们回顾一下定义(来自维基百科)。
抽象工厂
提供一个接口来创build相关或依赖对象的族,而不指定它们的具体类。
工厂方法
定义用于创build对象的接口 ,但让实现该接口的类决定实例化哪个类 。 Factory方法让类将实例化推迟到子类。
两种模式都允许将用户对象与创build所需实例(运行时解耦)分离,这是常见的方面。 这两种模式都可以根据任何特定的需求创build工厂的层次结构,这是另一个常见的方面。
抽象工厂允许在一个子类中创build几种不同types的实例,并在不同的子类中详细描述创build行为; 通常情况下,Factory方法声明只能根据子分类机制创build一个可以进行细化的对象types。 这是不同的。
通过总结。 假设Product定义了创build对象的超类,而ProductA和ProductB是两个不同的子类。 因此,抽象工厂方法将有两个方法createProductA()和createProductB(),它们将在其特定的子类中进行详细说明(就创build步骤而言):工厂子类详细说明两个定义的类的创build步骤创造中的对象。
根据上面的例子,工厂方法将以不同的方式实现,在许多工厂(每个工厂一个方法)中抽象创buildProductA和ProductB, 创build步骤的进一步专业化将在构build时被委托给层次结构。
如果我创build了一个抽象的(通过接口或抽象基类引用的) Factory Class来创build只有一个方法来创build对象的对象,那么它将是一个Factory Method 。
如果抽象工厂有超过1个方法来创build对象,那么它将是一个抽象工厂 。
比方说,我做了一个pipe理器,将处理MVC控制器的操作方法的需求。 如果它有一个方法,比方说创build将被用来创build视图模型的引擎对象,那么它将是一个工厂方法模式。 另一方面,如果它有两个方法:一个创build视图模型引擎,另一个创build动作模型引擎(或者任何你想调用的动作方法包含消费者的模型),那么它将是一个抽象工厂。
public ActionResult DoSomething(SpecificActionModel model) { var actionModelEngine = manager.GetActionModelEngine<SpecificActionModel>(); actionModelEngine.Execute(SpecificActionModelEnum.Value); var viewModelEngine = manager.GetViewModelEngine<SpecificViewModel>(); return View(viewModelEngine.GetViewModel(SpecificViewModelEnum.Value); }
虽然,自从StackOverflow的人在其他post(最年长到2009年)中对这个问题也提出质疑之后,我仍然找不到我想要的答案。
- 抽象工厂模式与工厂方法的区别
- 工厂和抽象工厂模式之间的基本区别是什么?
所以我通过networking做了几个小时的研究,回顾了一下这个例子,得出了这个结论,抽象工厂与工厂方法的主要区别是
- 意图:一致性或“外观” :抽象工厂的目的是将具有相同样式的对象(例如相同的外观UI小部件,相同样式的汽车部件,来自同一操作系统的对象,等等)抽象工厂的许多例子都提到关键词“相同的外观和感觉”。
- 形成更大组对象的对象 :抽象工厂创build一个对象族,形成一个更大的组对象,而不是一个对象。
- 稍后添加一个新的样式 :如果我们继续使用Factory Method,并尝试添加一组新的样式到现有的基础架构,这将是痛苦的。 有了抽象工厂,我们只需要创build一个实现抽象工厂类的新的具体工厂。
反例是
- 轿车中使用的跑车的一部分。 这种不一致可能会导致事故。
- 不同的操作系统GUI小部件中的Windows风格的button。 这不会破坏任何东西,但会伤害像我这样的一些人的用户体验。
- 后来我们发现我们的软件需要在下一次操作系统升级时运行,这需要不同的兼容系统对象,同时保持软件向后兼容。
因此,当一个最终的对象组应该具有相同的风格而没有一个对象的exception ,并且你想隐藏这个“保持相同样式”的细节时,我们应该使用抽象工厂。
据我了解抽象工厂和Factory方法定义的含义,第一个是在静态上下文中实现的,并根据input参数提供对象。
第二个使用已经创build的实现工厂方法接口的对象(家庭)。 工厂方法然后创build与原始对象相关的特定实例,而不pipe它是哪一个实例。
所以这通常会导致使用这两种模式在第一步你创build一个通用的对象,描述相关对象的家庭。 它由静态方法getInstance(“我的家族名称”)方法调用。 这种getInstance方法的实现决定了哪个家族对象将被创build。
然后我在新创build的family对象上调用createProduct()方法,并根据family对象返回新的产品。
似乎这些模式合作到每一个。
换句话说,抽象工厂主要关注“将会创build什么”以及工厂方法“如何”创build。
所有你必须记住的是抽象工厂是一个可以返回多个工厂的工厂 。 所以如果你有一个AnimalSpeciesFactory它可以返回这样的工厂:
Mamalfactory,BirdFactory,Fishfactory,ReptileFactory。 现在你有了一个来自AnimalSpeciesFactory的工厂,他们使用工厂模式来创build特定的目标文件。 例如,假设你从这个AnimalFactory获得了一个ReptileFactory,那么你可以提供创build爬行类的对象,例如:蛇,龟,蜥蜴对象。
工厂方法模式是创builddevise模式,它处理创build对象而不显示正在创build的对象的确切类别。 这种devise模式基本上允许类将实例推迟到子类。
抽象工厂模式为一组单个工厂提供封装,而不暴露具体的类。 在这个模型中,抽象工厂类的通用接口用于创build所需的具体对象,将对象的实现细节与其用法和构成分开。 这种devise模式在需要创build类似GUI组件的GUI应用程序中被广泛使用。
在谷歌上search时,我发现下面的博客里面都精彩地解释了devise模式。 看看这些