【问题标题】:Simple factory and Factory Method Design pattern difference简单工厂和工厂方法设计模式的区别
【发布时间】:2017-09-30 08:53:02
【问题描述】:

我正在学习新的设计模式,并试图了解简单工厂和工厂方法模式之间的区别。首先,我想明确一点,我尝试从 Stack-overflow 和其他网站上阅读很多关于相同内容的文章,但这对我没有帮助。

这是我的问题: 让我们考虑一下我有一个产品层次结构,如下所示:

我写了一个简单的工厂类,如下所示

public class SimpleItemFactory {

    static Item getItem(String type) {
        if(type.equals("Cake")) {
            return new Cake();
        }else if(type.equals("Chocolate")) {
            return new Chocolate();
        }else {
            return null;
        }
    }
}

所以现在我在一个地方创建了所有对象,所以如果明天发生任何变化[像构造函数需要一个参数],我们只需要在一个地方进行更改。但它打破了 OPEN CLOSED 原则,就好像明天我们添加更多项目我们需要在条件下更改 getItem() 方法。所以我们选择工厂方法模式

我们创建Factory类如下图:

public abstract class ItemFactory {
    abstract Item getItem();
}

class CakeFactory extends ItemFactory {
    @Override
    Item getItem() {
        return new Cake();
    }
}

class ChocolateFactory extends ItemFactory {
    @Override
    Item getItem() {
        return new Chocolate();
    }
}


class Client{
    public static void main(String[] args) {

        Item chocolate = new ChocolateFactory().getItem();
        System.out.println(chocolate);
    }
 }

现在,当客户想要添加名为 IceCream 的新 Item 时,他们只需创建名为 IceCreamFactory 的新工厂并从中创建 IceCream,如下所示:

class IceCreamFactory extends ItemFactory{

    @Override
    Item getItem() {
        return new IceCream();
    }

}

class Client{
    public static void main(String[] args) {
        Item iceCream = new IceCreamFactory().getItem();
        System.out.println(iceCream);

    }
}

我的理解正确吗? 我们在这里满足了开放封闭原则,但是对于每个产品(Item)我们都需要一个工厂类,这不是可以管理的噩梦吗?

注意:文章我指的是https://www.codeproject.com/Articles/1135918/Factory-Patterns-Factory-Method-Pattern?msg=5380215#xx5380215xx

【问题讨论】:

  • 简单工厂违反 OCP。如果你选择 Simple Factory,你最终会添加几十个 if-else(以最简单的形式),我认为这不是最佳实践。在这种情况下,我宁愿选择工厂方法

标签: java design-patterns factory-pattern software-design


【解决方案1】:

您的理解其实是正确的,只是您需要注意的是,每个设计模式都是为了解决至少一个问题,有时它可能会带来一些其他的复杂性副作用,这意味着没有完美的设计模式可以解决所有问题。

出于学习目的,您将一个一个地应用设计模式(这使得设计模式的真正力量变得害羞或隐藏),但是在现实世界的上下文中,设计模式混合在一起(甚至您可以发明一种新的模式) :p) 为了创造最能满足您的需求并接近理想的东西,您可以满足例如 Builder 模式与 Factory 模式或 Factory 模式与 Strategy 模式的混合,甚至将它们三者混合在一起......

对于您的情况,我建议例如使用 Factory Method PatternSimple Factory Pattern 结合 Dependency Injection Pattern 来创建一些东西非常漂亮,同时满足开闭原则。

class ItemFactoryContainer() {

    private Map<String, ItemFactory> factories = new HashMap<>();

    public void register(String name, ItemFactory factory) {
        factories.put(name, factory);
    }

    public ItemFactory getFactory(String name) {
        return factories.get(name);
    }
}

public class ItemFactoryTest {

    public static void main(String...args) {

        ItemFactoryContainer factoryContainer = new ItemFactoryContainer();

        factoryContainer.register("choclate", new ChocolateFactory ());
        factoryContainer.register("cake", new CakeFactory ());
        factoryContainer.register("iceCream", new IceCreamFactory  ());

        Chocolate choclate = factoryContainer.getFactory("choclate").getItem();
        Cake cake = factoryContainer.getFactory("cake").getItem();
    }
}

【讨论】:

  • 太棒了,您的明显意思工厂方法有时变得难以管理。还有一个问题在你的例子中,你不认为明天我想再添加一个名为 Donuts 的项目,然后在 ItemFactoryTest 我需要添加/更改 factoryContainer 以获取 Donuts 工厂
  • 我的意思是一些设计模式可能会带来一些其他副作用来解决问题,例如增加类的数量从而增加代码源的大小......
  • 添加一个甜甜圈项目就像factoryContainer.register("Donuts", new DonutsFactory ()); 一样简单,它不违反打开/关闭原则(这实际上被认为是扩展而不是修改),在现实世界的应用程序中,您可能会使用管理注册部分Spring framework 通过 XML 配置。
  • @MouadELFakir 完全是修改,非扩展
【解决方案2】:

答案(@Mouad EL Fakir 和 @François LEPORCQ)是关于静态工厂的。这是您的第一部分代码。

还有两种风格,分别是Factory MethodAbstract Factory

您的代码的第二部分是使用 CakeFactory 和 ChocolateFactory,您正在尝试使用工厂方法(尽管您的命名建议使用抽象工厂)。这里工厂方法getXXX()的使用是不正确的。这些模式的目的是不同的。在您的代码中,除了增加类的数量之外,您没有实现任何目标。 (尽管增加的类数量并行层次结构被记录为与工厂相关的成本)

我最近回答了一个类似的问题,看看这些答案是否有帮助

Role of Creator in Factory Pattern

What are the real benefits of using the Abstract Factory in the following example, instead of the factory method?

【讨论】:

  • 你能解释一下这里的 getXXX() 方法有什么不正确的地方吗?
  • @vicky 我已经更新了我对第一个问题的回答(工厂模式中创建者的角色),以响应类似的请求。请阅读其中提到使用 getXXX() 方法的答案的更新部分。不想在这里重复。
【解决方案3】:

我认为在您的示例中,工厂似乎没用,因为您的 Cake 和 Chocolate 构造函数不需要任何参数。

当您想要构造具有多个 args 的复杂对象以遵守 DIP(依赖倒置原则)并避免尴尬的耦合时,工厂非常有用。

此外,您的示例似乎违反了 LSP(Liskov 替换原则)。 Cake 和 Chocolate 类没有任何共同点,作为证据,您的抽象超类名为 Item,这是一个非常通用的名称。

对我来说,对于复杂的不常见的对象,按类选择一个工厂更好,并且有效地尊重OCP(Open Closed Principle)。

您的第一个示例可用于实例化继承相同超类的类,但使用您的类作为 arg 和反射(使用 java.lang.reflect)来调用构造函数,而不仅仅是使用字符串求值。 (您的 IDE 无法自动完成)并且在不匹配的情况下不返回 null,抛出 IllegalArgumentException,它更正确。

像这样:

import java.lang.reflect.Constructor;

public class SimpleItemFactory {
    public <T extends Item> T createItem(Class<? extends Item> itemClass) {
        try {
            // construct your dependencies here
            // and inject them in the getConstructor
            Constructor<? extends Item> constructor = itemClass.getConstructor();
            return (T)constructor.newInstance();
        } catch (Exception e) {
            throw new IllegalArgumentException();
        }
    }
}

并像这样使用它:

class Client {
    public static void main(String[] args) {
        SimpleItemFactory factory = new SimpleItemFactory();
        IceCream iceCream = factory.createItem(IceCream.class);
        System.out.println(iceCream);
        Cake cake = factory.createItem(Cake.class);
        System.out.println(cake);
        Chocolate chocolate = factory.createItem(Chocolate.class);
        System.out.println(chocolate);
    }
}

如果您没有依赖项,则可以直接在抽象 Item 类中将 createItem() 方法实现为静态方法。

看看这本书,这是一个很好的资源:@​​987654321@

OCP 就是 Open for extension Close for modify 的原则。如果您需要打开工厂进行修改以添加新类(在 switch 或 if 中管理的新案例),不,您的工厂不是“OCP”。

您不应该在构造函数中计算税金,构造函数应该只是...构造一个对象。您可以使用类似策略的模式来拥有许多 taxCalculator 并将它们注入到构造函数中,并带有 IOC(Inversion of控制)机制,如依赖注入或以......工厂的简单方式(这是可以接受的),但不是在您的类中使用简单的静态方法。

对不起,我的英语不好,我很难回答这样一个复杂的问题。

【讨论】:

  • 嗨@françois-leporcq,对不起,我仍然没有明白你的答案,工厂方法模式是否遵循开闭原则?让我们考虑 Item 类具有复杂的构造函数逻辑,例如在构造函数中计算税,那么我们应该怎么做?选择工厂方法还是简单的工厂?
猜你喜欢
  • 1970-01-01
  • 2011-05-11
  • 2011-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-12
  • 1970-01-01
  • 2017-07-31
相关资源
最近更新 更多