工厂模式。什么时候使用工厂方法?

什么时候在对象中使用工厂方法而不是factory类是一个好主意?

200690 次浏览

这真的是个品味问题。工厂类可以根据需要进行抽象/接口,而工厂方法是轻量级的(而且往往是可测试的,因为它们没有定义的类型,但它们需要一个众所周知的注册点,类似于服务定位器,但用于定位工厂方法)。

当它们返回的对象类型具有私有构造函数时,当不同的工厂类在返回的对象上设置不同的属性时,或者当特定的工厂类型与其返回的具体类型耦合时,工厂类非常有用。

WCF使用ServiceHostFactory类来检索不同情况下的ServiceHost对象。IIS使用标准的ServiceHostFactory来检索.svc文件的ServiceHost实例,但是WebScriptServiceHostFactory用于向JavaScript客户端返回序列化的服务。ADO。NET Data Services有自己特殊的DataServiceHostFactory和ASP。NET有它的ApplicationServicesHostFactory,因为它的服务有私有构造函数。

如果只有一个类在使用工厂,那么可以在该类中使用工厂方法。

工厂类更重量级,但也有一定的优势。当您需要从多个原始数据源构建对象时,它们允许您在一个地方只封装构建逻辑(可能还包括数据的聚合)。在那里可以进行抽象的测试,而不需要考虑对象接口。

我发现这是一种有用的模式,特别是当我无法替换ORM且ORM不足,并且希望有效地实例化来自DB表连接或存储过程的许多对象时。

当您需要几个具有相同参数类型但具有不同行为的“构造函数”时,它们也很有用。

工厂方法应该被认为是构造函数的替代品——尤其是在构造函数表达能力不够的时候。

class Foo{
public Foo(bool withBar);
}

表现力不如:

class Foo{
public static Foo withBar();
public static Foo withoutBar();
}

当你需要一个复杂的过程来构造对象时,当构造需要一个你不想要的实际类的依赖关系时,当你需要构造不同的对象时,工厂类是有用的。

我喜欢从我的类是“人”的角度来考虑设计模式,而模式是人们彼此交谈的方式。

所以,对我来说,工厂模式就像一个招聘机构。你的公司需要不同数量的工人。这个人可能知道一些他们需要雇佣的人的信息,但仅此而已。

所以,当他们需要一个新员工时,他们会打电话给招聘机构,告诉他们他们需要什么。现在,要真正雇佣某人,你需要知道很多东西-福利,资格验证,等等。但是招聘的人不需要知道这些——招聘机构会处理所有这些。

以同样的方式,使用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的依赖关系,除了来自消费者的字符串数据。

我个人认为单独的Factory类有意义的一种情况是,当您试图创建的最终对象依赖于其他几个对象时。例如,在PHP中:假设你有一个House对象,它又有一个Kitchen和一个LivingRoom对象,而LivingRoom对象内部也有一个TV对象。

实现这一点的最简单方法是让每个对象在它们的construct方法上创建它们的子对象,但如果属性是相对嵌套的,当你的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时,这些库或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();
}
}

我把工厂比作图书馆的概念。例如,您可以有一个库用于处理数字,另一个库用于处理形状。可以将这些库的函数存储在逻辑命名为NumbersShapes的目录中。这些是泛型类型,可以包括整数,浮点数,双元,长或矩形,圆形,三角形,在形状的情况下,五边形。

该系统采用了多态、依赖注入和控制反转等技术。

工厂模式的目的是:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

假设你正在构建一个操作系统或框架,你正在构建所有的离散组件。

下面是PHP中工厂模式概念的一个简单示例。我可能不会完全理解,但这只是一个简单的例子。我不是专家。

class NumbersFactory {
public static function makeNumber( $type, $number ) {
$numObject = null;
$number = null;


switch( $type ) {
case 'float':
$numObject = new Float( $number );
break;
case 'integer':
$numObject = new Integer( $number );
break;
case 'short':
$numObject = new Short( $number );
break;
case 'double':
$numObject = new Double( $number );
break;
case 'long':
$numObject = new Long( $number );
break;
default:
$numObject = new Integer( $number );
break;
}


return $numObject;
}
}


/* Numbers interface */
abstract class Number {
protected $number;


public function __construct( $number ) {
$this->number = $number;
}


abstract public function add();
abstract public function subtract();
abstract public function multiply();
abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
public function add() {
// implementation goes here
}


public function subtract() {
// implementation goes here
}


public function multiply() {
// implementation goes here
}


public function divide() {
// implementation goes here
}
}
/* Integer Implementation */
class Integer extends Number {
public function add() {
// implementation goes here
}


public function subtract() {
// implementation goes here
}


public function multiply() {
// implementation goes here
}


public function divide() {
// implementation goes here
}
}
/* Short Implementation */
class Short extends Number {
public function add() {
// implementation goes here
}


public function subtract() {
// implementation goes here
}


public function multiply() {
// implementation goes here
}


public function divide() {
// implementation goes here
}
}
/* Double Implementation */
class Double extends Number {
public function add() {
// implementation goes here
}


public function subtract() {
// implementation goes here
}


public function multiply() {
// implementation goes here
}


public function divide() {
// implementation goes here
}
}
/* Long Implementation */
class Long extends Number {
public function add() {
// implementation goes here
}


public function subtract() {
// implementation goes here
}


public function multiply() {
// implementation goes here
}


public function divide() {
// implementation goes here
}
}


$number = NumbersFactory::makeNumber( 'float', 12.5 );

在以下情况下,在对象内部使用工厂方法是个好主意:

  1. 对象的类不知道它必须创建什么确切的子类
  2. Object的类被设计成它创建的对象由子类指定
  3. 对象的类将其职责委托给辅助子类,并且不知道确切的类将承担这些职责

在以下情况下使用抽象工厂类是个好主意:

  1. 对象不应该依赖于其内部对象是如何创建和设计的
  2. 一组链接的对象应该一起使用,您需要满足这个约束
  3. 对象应该由几个可能的链接对象家族中的一个来配置,这些对象将成为父对象的一部分
  4. 它需要共享只显示接口而不显示实现的子对象

考虑一个场景,当您必须设计Order和Customer类时。为了简单和初始需求,您不需要Order类的factory,并使用许多“new Order()”语句填充应用程序。一切都很顺利。

现在出现了一个新的需求,即Order对象不能在没有客户关联的情况下实例化(新的依赖项)。现在你有以下考虑。

1-你创建了只对新实现有效的构造函数重载。(不可接受)。 2-您更改Order()签名并更改每个调用。(不是一个很好的练习,真正的痛苦)。

相反,如果你已经为Order Class创建了一个工厂,你只需要修改一行代码就可以了。我建议几乎所有聚合关联都使用Factory类。希望这能有所帮助。

UML从

enter image description here

产品:它定义Factory方法创建的对象的接口。

实现Product接口

创造者:声明Factory方法

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<String,Game> games = new HashMap<String,Game>();
/*
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

此示例通过实现FactoryMethod. c类来显示Factory类。

  1. Game是所有类型游戏的接口。它定义了复杂方法:createGame()

  2. Chess, Ludo, Checkers是游戏的不同变体,它们提供了createGame()的实现

  3. public Game getGame(String gameName)IGameFactory类中的FactoryMethod

  4. GameFactory在构造函数中预先创建不同类型的游戏。它实现了IGameFactory工厂方法。

  5. game Name作为命令行参数传递给NotStaticFactoryDemo

  6. GameFactory中的getGame接受游戏名称并返回相应的Game对象。

工厂:

创建对象而不向客户端公开实例化逻辑。

FactoryMethod

定义一个用于创建对象的接口,但是让子类来决定实例化哪个类。Factory方法允许类延迟实例化到子类

用例:

Client不知道在运行时需要创建什么具体的类,而只是想获得一个可以完成这项工作的类。

如果你想在使用方面创建一个不同的对象。它很有用。

public class factoryMethodPattern {
static String planName = "COMMERCIALPLAN";
static int units = 3;
public static void main(String args[]) {
GetPlanFactory planFactory = new GetPlanFactory();
Plan p = planFactory.getPlan(planName);
System.out.print("Bill amount for " + planName + " of  " + units
+ " units is: ");
p.getRate();
p.calculateBill(units);
}
}


abstract class Plan {
protected double rate;


abstract void getRate();


public void calculateBill(int units) {
System.out.println(units * rate);
}
}


class DomesticPlan extends Plan {
// @override
public void getRate() {
rate = 3.50;
}
}


class CommercialPlan extends Plan {
// @override
public void getRate() {
rate = 7.50;
}
}


class InstitutionalPlan extends Plan {
// @override
public void getRate() {
rate = 5.50;
}
}


class GetPlanFactory {


// use getPlan method to get object of type Plan
public Plan getPlan(String planType) {
if (planType == null) {
return null;
}
if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
return new DomesticPlan();
} else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
return new CommercialPlan();
} else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
return new InstitutionalPlan();
}
return null;
}
}

任何将对象创建延迟到其需要使用的对象的子类的类都可以视为Factory模式的示例。

我在https://stackoverflow.com/a/49110001/504133的另一个答案中详细提到过

我认为这取决于你想要给你的代码带来的松耦合程度。

工厂方法解耦得很好,但是工厂类不行。

换句话说,使用工厂方法比使用简单的工厂(称为工厂类)更容易更改内容。

看看这个例子:https://connected2know.com/programming/java-factory-pattern/。现在,想象一下你想要带来一个新的动物。在Factory类中,您需要更改Factory,但在Factory方法中,不需要,您只需要添加一个新的子类。

enter image description here 假设你有不同的客户,他们有不同的偏好。有人需要大众、奥迪等等。有一件事是共同的——它是一辆汽车

为了让我们的客户满意,我们需要一个工厂。工厂只需要知道客户想要哪一辆车,并将其交付给客户。如果以后我们有另一辆车,我们可以很容易地扩大我们的停车场和工厂。

下面你可以看到一个例子(ABAP): enter image description here < / p > 现在我们将创建一个工厂实例,并监听客户的愿望。 enter image description here < / p >

我们只用一个create()方法创建了三种不同的汽车。

结果:

enter image description here

如果你想让逻辑更清晰,程序更可扩展,工厂模式通常非常有用。

我简短的解释是,当我们没有足够的信息来创建一个具体的对象时,我们使用工厂模式。我们要么不知道依赖关系,要么不知道对象的类型。我们几乎总是不知道它们因为这是运行时的信息。

示例:我们知道我们必须创建一个车辆对象,但我们不知道它是飞行还是在地面上工作。

GOF定义:

定义一个用于创建对象的接口,但是让子类来决定实例化哪个类。工厂方法允许类延迟实例化到子类。

通用的示例:

public abstract class Factory<T> {


public abstract T instantiate(Supplier<? extends T> supplier);


}

具体类

public class SupplierFactory<T> extends Factory<T> {


@Override
public T instantiate(Supplier<? extends T> supplier) {
return supplier.get();
}
}

实现

public class Alpha implements BaseInterface {
@Override
public void doAction() {
System.out.println("The Alpha executed");
}
}


public class Beta implements BaseInterface {
@Override
public void doAction() {
System.out.println("The Beta executed");
}
}


public interface BaseInterface {
void doAction();
}


public class Main {
public static void main(String[] args) {
Factory<BaseInterface> secondFactory = new SupplierFactory<>();
secondFactory.instantiate(Beta::new).doAction();
secondFactory.instantiate(Alpha::new).doAction();
}
}

短暂的优势

  • 您正在分离可以变化的代码和不变的代码(即,使用简单工厂模式的优点仍然存在)。这种技术可以帮助您轻松地维护代码。
  • 你的代码不是紧密耦合的;因此,你可以随时在系统中添加新的类,如Lion、Beer等,而无需修改现有的体系结构。因此,您遵循了“修改封闭,扩展开放”的原则。