何时使用工厂方法模式?

42

何时使用工厂方法模式?

请提供一些具体的想法,什么情况下在项目中使用它?以及它如何比使用 new 关键字更好?

10个回答

47

当您希望重用不同组件的常见功能时,请使用工厂方法(而不是抽象工厂)。

示例:想象一下,您有一支M16步枪。就像这样:

public class M16
{
    private Scope scope = new StandardScope();
    private SecondaryWeapon secondary = new Bayonet();
    private Camouflage camo = new DesertCamo();

    public double getMass()
    {
        // Add the mass of the gun to the mass of all the attachments.
    }

    public Point2D shootAtTarget(Point2D targetPosition)
    {
        // Very complicated calculation taking account of lots of variables such as
        // scope accuracy and gun weight.
    }
}

您可能暂时对此感到满意,认为您不需要改变任何东西。但是当您必须在丛林中进行秘密夜间隐身任务时,您会意识到您的附件完全不适合。您真正需要的是夜视镜、丛林迷彩和榴弹发射器作为辅助武器。您将不得不从原始M16复制并粘贴代码......这样的可扩展性不好......工厂方法来拯救!

重写您的M16类:

public abstract class M16
{
    private Scope scope = getScope();
    private SecondaryWeapon secondary = getSecondaryWeapon();
    private Camouflage camo = getCamouflage();

    public double getMass()
    {
        // Add the mass of the gun to the mass of all the attachments.
    }

    public Point2D shootAtTarget(Point2D targetPosition)
    {
        // Very complicated calculation taking account of lots of variables such as
        // scope accuracy and gun weight.
    }

    // Don't have to be abstract if you want to have defaults.
    protected abstract Scope getScope();
    protected abstract SecondaryWeapon getSecondaryWeapon();
    protected abstract Camouflage getCamouflage();
}


//Then, your new JungleM16 can be created with hardly any effort (and importantly, no code //copying):

public class JungleM16 : M16
{
    public Scope getScope()
    {
        return new NightVisionScope();
    }

    public SecondaryWeapon getSecondaryWeapon()
    {
        return new GrenadeLauncher();
    }

    public Camouflage getCamouflage()
    {
        return new JungleCamo();
    }
}

主要思想?定制和更换组成对象,同时保持共同功能。

实际使用场景: 您刚刚设计了一个非常酷的GUI,并且它具有非常复杂的布局。如果您想要不同的小部件,则必须重新布局所有内容,这将是一种真正的痛苦。因此...使用工厂方法创建小部件。然后,如果您改变主意(或其他人想使用您的类,但使用不同的组件),您只需子类化GUI并覆盖工厂方法即可。


3
嘿,这是一个很好的解释。你写过任何教程吗?我很想阅读它们! - Owais Qureshi
1
嘿,伙计,解释得非常好。这让理解变得更容易了。干得好。 - Flavio
终于有一个有意义的工厂模式。我看到了很多很糟糕的例子,对代码库毫无好处。 - Pue-Tsuâ
看起来像是抽象工厂设计模式,但不是工厂方法。 - zzfima

38

我有两种情况经常使用它:

  1. 对象需要以某种特定方式初始化
  2. 当我想要构造一个基于抽象类型(抽象类或接口)的具体类型时。

举例:

  1. 第一种情况可以是您想创建一个工厂来创建 SqlCommand 对象,在返回命令对象之前自动附加有效的 SqlConnection

  2. 第二种情况是如果您定义了一个接口,并在执行时确定要使用哪个精确的接口实现(例如通过在配置文件中指定)。


23
您可以参考《框架设计准则第二版》第9.5节的“工厂”一章。以下是关于使用工厂而非构造函数的建议摘录:

DO:更倾向于使用构造函数而不是工厂方法,因为它们通常比特定的创建机制更易用、一致和方便。

CONSIDER:如果需要比构造函数更多的控制来创建实例,则可以考虑使用工厂方法。

DO:在开发人员可能不知道要构造哪种类型(例如针对基类或接口编码)的情况下使用工厂方法。

CONSIDER:当只有通过一个命名方法才能使操作变得清晰明了时,可以考虑使用工厂方法。

DO:对于转换式操作,请使用工厂方法。

此外,从第5.3节“构造函数设计”中可以得出以下建议:

CONSIDER:如果所需操作的语义不能直接映射到新实例的构造,或遵循构造函数设计准则感觉不自然,则可以考虑使用静态工厂方法。


20

尽管这不一定是它的主要用途,但它对于某些需要使用类的专门实例的情况非常有用:

public ITax BuildNewSalesTax()
public ITax BuildNewValueAddedTax()

您需要使用两种方法来构建一个税务对象,但是您不希望每次都依赖于使用 "new",因为构造函数可能很复杂。通过这种方式,我将所有更改封装在一个单独的方法中,以便于他人进行未来维护。


5

我在使用工厂模式时

  1. 当一个类不知道它必须创建哪个类的对象时。

  2. 一个类指定其子类以指定要创建哪些对象。

  3. 在程序员语言中(非常原始的形式),您可以使用工厂模式,在提供数据的情况下创建任何一个子类的对象之一。


3

1

当系统应该独立于其产品的创建、组合和表示方式时,使用抽象工厂模式。 当系统应配置为使用多个产品系列之一时。 当设计了一组相关的产品对象以一起使用,并且您需要强制执行此约束时。 当您想提供一个产品类库,并且只想公开它们的接口而不是实现时,请使用抽象工厂模式。


0

使用工厂方法模式而不是new关键字更好。其思想是将对象的完整实例化移出业务逻辑。这个原则是依赖注入的核心。而且,工厂方法的工作可以在以后委托给依赖注入框架,如Spring.net或Castle Windsor。


1
我曾经在一个代码库中工作,其中每个“new”都被替换为一个工厂委托,完全IoC容器化。这是一场噩梦。在与5个开发人员共同开发了一年半后,它被废弃了。功能被3个开发人员使用行业标准实践在1个月内替换。从我的经验来看,在@Dzmitri指出的情况下,工厂非常有用。除非您计划通过配置更改类型或具有非常有趣的构造函数逻辑,否则我认为用工厂替换new是过度设计和低效的。 - Fred

-1
为了回答你问题的第二部分,我认为工厂方法比“new”关键字更好的原因是,它减少了对特定类构造函数的依赖性。通过使用工厂方法,你将所需对象的创建委托给其他人,因此调用者不需要知道如何创建对象。

-2

我认为这是当你希望你的应用程序在未来可以松散耦合和可扩展而无需进行编码更改时使用的。

我在博客上写了一篇关于为什么我在项目中选择工厂模式的文章,也许它可以给你更多的见解。示例是用PHP编写的,但我认为它通常适用于所有语言。

http://www.mixedwaves.com/2009/02/implementing-factory-design-pattern/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接