什么时候在对象中使用工厂方法而不是Factory类是个好主意?
我喜欢在我的课程"人"方面考虑设计模式,模式是人们互相交谈的方式.
所以,对我而言,工厂模式就像一个招聘机构.你有一个人需要不同数量的工人.这个人可能知道他们雇用的人需要的一些信息,但就是这样.
因此,当他们需要新员工时,他们会致电招聘机构并告诉他们他们需要什么.现在,要真正雇用某人,你需要了解很多东西 - 福利,资格验证等.但招聘人员不需要知道任何这些 - 招聘机构处理所有这些.
以同样的方式,使用Factory允许消费者创建新对象,而不必知道他们如何创建或者他们的依赖关系的细节 - 他们只需要提供他们实际想要的信息.
public interface IThingFactory { Thing GetThing(string theString); } public class ThingFactory : IThingFactory { public Thing GetThing(string theString) { return new Thing(theString, firstDependency, secondDependency); } }
所以,现在ThingFactory的消费者可以获得Thing,而不必了解Thing的依赖关系,除了来自消费者的字符串数据.
工厂方法应该被视为构造函数的替代 - 主要是当构造函数不够表达时,即.
class Foo{ public Foo(bool withBar); }
不像以下那样富有表现力:
class Foo{ public static Foo withBar(); public static Foo withoutBar(); }
当你需要一个复杂的构造对象的过程,当构造需要一个你不想要的实际类的依赖,当你需要构造不同的对象等时,工厂类很有用.
我个人发现单独的Factory类有意义的一种情况是,您尝试创建的最终对象依赖于其他几个对象.例如,在PHP中:假设您有一个House
对象,该对象又有一个Kitchen
和一个LivingRoom
对象,并且该LivingRoom
对象也有一个TV
对象.
实现这一目标的最简单方法是让每个对象在它们的构造方法上创建它们的子对象,但是如果属性是相对嵌套的,那么当你House
创建失败时,你可能会花一些时间试图准确地找出失败的对象.
另一种方法是执行以下操作(依赖注入,如果您喜欢花哨的术语):
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
在这里,如果创建House
失败的过程只有一个地方可以看,但每次想要一个新的时候必须使用这个块House
远非方便.输入工厂:
class HouseFactory {
public function create() {
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
return $HouseObj;
}
}
$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();
感谢工厂在这里创建一个House
抽象的过程(因为你不需要创建和设置每个单独的依赖关系,当你只想创建一个House
),同时集中,这使得它更容易维护.使用单独的工厂可能有益(例如可测试性)还有其他原因,但我发现这个特定的用例可以最好地说明工厂类如何有用.
重要的是要明确区分使用工厂或工厂方法背后的想法.两者都旨在解决互斥的不同类型的对象创建问题.
让我们具体谈谈"工厂方法":
首先,当您开发库或API时,它们将用于进一步的应用程序开发,那么工厂方法是创建模式的最佳选择之一.背后的原因; 我们知道何时创建一个具有所需功能的对象,但是对象的类型将保持未定,否则将决定传递动态参数.
现在重点是,通过使用工厂模式本身可以实现大致相同,但是如果工厂模式将用于上面突出显示的问题,那么将在系统中引入一个巨大的缺点,那就是你创建不同对象(子类对象)的逻辑将是特定于某些业务条件,以便将来当您需要扩展库的其他平台的功能时(在技术上,您需要添加更多基类或抽象类的子类,因此工厂除了现有的对象之外还将返回这些对象基于一些动态参数)然后每次你需要改变(扩展)工厂类的逻辑,这将是昂贵的操作,而不是从设计的角度来看.另一方面,如果"工厂方法"模式将用于执行相同的操作,那么您只需创建其他功能(子类)并通过注入动态注册,这不需要更改基本代码.
interface Deliverable
{
/*********/
}
abstract class DefaultProducer
{
public void taskToBeDone()
{
Deliverable deliverable = factoryMethodPattern();
}
protected abstract Deliverable factoryMethodPattern();
}
class SpecificDeliverable implements Deliverable
{
/***SPECIFIC TASK CAN BE WRITTEN HERE***/
}
class SpecificProducer extends DefaultProducer
{
protected Deliverable factoryMethodPattern()
{
return new SpecificDeliverable();
}
}
public class MasterApplicationProgram
{
public static void main(String arg[])
{
DefaultProducer defaultProducer = new SpecificProducer();
defaultProducer.taskToBeDone();
}
}
当您需要具有相同参数类型但具有不同行为的多个"构造函数"时,它们也很有用.
在以下情况下使用对象内部的工厂方法是个好主意:
Object的类不知道它必须创建什么确切的子类
Object的类被设计为使其创建的对象由子类指定
Object的类将其职责委托给辅助子类,并且不知道确切的类将承担这些职责
在以下情况下使用抽象工厂类是个好主意:
您的对象不应该依赖于其内部对象的创建和设计方式
应该一起使用一组链接对象,您需要提供此约束
对象应该由几个可能的链接对象系列之一配置,这些链接对象将成为父对象的一部分
需要共享仅显示接口但不是实现的子对象
UML来自
Product:它定义Factory方法创建的对象的接口.
ConcreteProduct:实现产品界面
创建者:声明工厂方法
ConcreateCreator: 实现Factory方法以返回ConcreteProduct的实例
问题陈述:使用工厂方法创建游戏工厂,工厂方法定义游戏界面.
代码段:
import java.util.HashMap;
/* Product interface as per UML diagram */
interface Game{
/* createGame is a complex method, which executes a sequence of game steps */
public void createGame();
}
/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
public Chess(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Chess game");
System.out.println("Opponents:2");
System.out.println("Define 64 blocks");
System.out.println("Place 16 pieces for White opponent");
System.out.println("Place 16 pieces for Black opponent");
System.out.println("Start Chess game");
System.out.println("---------------------------------------");
}
}
class Checkers implements Game{
public Checkers(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Checkers game");
System.out.println("Opponents:2 or 3 or 4 or 6");
System.out.println("For each opponent, place 10 coins");
System.out.println("Start Checkers game");
System.out.println("---------------------------------------");
}
}
class Ludo implements Game{
public Ludo(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Ludo game");
System.out.println("Opponents:2 or 3 or 4");
System.out.println("For each opponent, place 4 coins");
System.out.println("Create two dices with numbers from 1-6");
System.out.println("Start Ludo game");
System.out.println("---------------------------------------");
}
}
/* Creator interface as per UML diagram */
interface IGameFactory {
public Game getGame(String gameName);
}
/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {
HashMap games = new HashMap();
/*
Since Game Creation is complex process, we don't want to create game using new operator every time.
Instead we create Game only once and store it in Factory. When client request a specific game,
Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
*/
public GameFactory(){
games.put(Chess.class.getName(),new Chess());
games.put(Checkers.class.getName(),new Checkers());
games.put(Ludo.class.getName(),new Ludo());
}
public Game getGame(String gameName){
return games.get(gameName);
}
}
public class NonStaticFactoryDemo{
public static void main(String args[]){
if ( args.length < 1){
System.out.println("Usage: java FactoryDemo gameName");
return;
}
GameFactory factory = new GameFactory();
Game game = factory.getGame(args[0]);
if ( game != null ){
game.createGame();
System.out.println("Game="+game.getClass().getName());
}else{
System.out.println(args[0]+ " Game does not exists in factory");
}
}
}
输出:
java NonStaticFactoryDemo Chess --------------------------------------- Create Chess game Opponents:2 Define 64 blocks Place 16 pieces for White opponent Place 16 pieces for Black opponent Start Chess game --------------------------------------- Game=Chess
此示例Factory
通过实现a来显示一个类FactoryMethod
.
Game
是所有类型游戏的界面.它定义了复杂的方法:createGame()
Chess, Ludo, Checkers
是游戏的不同变体,提供实现 createGame()
public Game getGame(String gameName)
是FactoryMethod
在IGameFactory
类
GameFactory
在构造函数中预先创建不同类型的游戏.它实现了IGameFactory
工厂方法.
游戏名称作为命令行参数传递给 NotStaticFactoryDemo
getGame
in GameFactory
接受游戏名称并返回相应的Game
对象.
厂:
创建对象而不将实例化逻辑暴露给客户端.
FactoryMethod
定义用于创建对象的接口,但让子类决定实例化哪个类.Factory方法允许类将实例化延迟到子类
使用案例:
何时使用:Client
不知道在运行时需要创建哪些具体类,但只是想获得一个可以完成工作的类.
这真的是品味问题.工厂类可以根据需要进行抽象/接口,而工厂方法的重量较轻(并且也往往是可测试的,因为它们没有定义的类型,但它们需要一个众所周知的注册点,类似于服务定位器,但用于定位工厂方法).