使用工厂方法模式相较于简单工厂模式有哪些优势?

3
我正在阅读有关工厂方法模式和简单工厂的内容。据我所了解,简单工厂已经足够,并且我看不出工厂方法模式的用例。请阅读此链接https://www.binpress.com/factory-design-pattern/,然后我会问我的问题。
1)在简单工厂中,它说它很糟糕,因为它违反了开闭原则。我理解这一点,但在工厂方法模式中,它所做的仍然违反了开闭原则。
if ('car'==$toyName) {
            $toy = new NyCar();
        } else if ('helicopter'==$toyName) {
            $toy = new NyHelicopter();
        }

如果纽约的Tory有新的改动,我们需要在这里添加它。
2) 阅读链接后,在实际找到更好的解决方案之前,它使用了以下代码:
class NySimpleFactory {
    public function createToy($toyName) {
        $toy = null;

        if ('car'==$toyName) {
            $toy = new NyCar();
        } else if ('helicopter'==$toyName) {
            $toy = new NyHelicopter();
        }

        return $toy;
    }
}

class NyToysFactory {

    public $simpleFactory;

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

    public function produceToy($toyName) {
        $toy = null;
        $toy = $this->simpleFactory->createToy($toyName);
        $toy->prepare();
        $toy->package();
        $toy->label();
        return $toy;
    }
}

然后它说,

开发人员很快完成了新代码并将其交给美国工厂。两周后,开发人员办公室里的电话开始响起,因为纽约工厂正在生产问题。原来,由于工作人员不想进行包装和标签工作,遥远分支的开发人员修改了NyToysFactory类的produceToy()方法,删除了它的label()和package()函数。

看起来简单工厂在这种情况下行不通。我们不希望美国的分支机构能够修改produceToy()函数。ProduceToy()应该由一组标准程序组成,分支机构只负责创建特定地点的玩具。如果他们能够创建一个抽象类呢?他们创建的抽象类将有一个具体的produceToy()方法,该方法将实现所有分支机构都必须遵循的一组标准操作程序。在produceToy()内部,它调用自己的抽象方法createToy()来获取一个玩具类。这样,createToy()就能够封装对象的创建,并且由于它是抽象的,它将对象的创建委托给其子类。

问题是:a)什么意思是将其移交给美国工厂?b)或者我们不希望美国的分支机构能够修改produceToy()函数。如果他们仍然可以修改produceToy函数,那么这有什么区别吗?我只是不明白为什么简单工厂在以下示例中不好。

无需阅读此链接中的抽象工厂

1个回答

2

这段代码/问题没有展示抽象工厂或工厂方法。通过在参数上进行开关以决定实例化哪个类确实是一种反模式,鼓励违反开闭原则。

抽象工厂

抽象工厂主要是为了强制实现一组相关的类:

abstract class ToyFactory
+ createBear(): ToyBear
+ createElephant(): ToyElephant

class USToyFactory extends ToyFactory
+ createBear(): ToyBear -> creates USToyBear
+ createElephant(): ToyElephant -> USToyElephant

abstract class ToyBear
+ playSound()

class USToyBear extends ToyBear
+ playSound()//play US National Anthem

当需要一个ToyFactory时,传递一个USToyFactory可以强制创建美国玩具(USToyBear和USToyElephant)- 这就是抽象工厂的威力。

请注意,产品(熊,大象等)已经提前知道了(Ahead of Time)。

工厂方法

工厂方法是将实例化推迟到子类的过程。

abstract class Dashboard
+ createWidget(): Widget

abstract class Widget
+ config()

class StatsDashboard extends Dashboard
+ createWidget: Widget -> return new StatsWidget()

class StatsWidget extends Widget

调用createWidget()将返回一个Widget,但要返回哪个具体的Widget必须延迟到子类(StatsDashboard返回一个StatsWidget)。
注意创建方法是在继承树上声明的,但它们在继承树下被实现。
❧ 一个敏锐的读者会发现Abstract Factory方法看起来像Factory Methods,这是巧合吗?-不是。这就是Factory Method名称的来源(它们实现具体类的实例化)。
关于“美国工厂”的混淆是合理的;这是糟糕的用词选择。作者暗示这段代码可能被传递给工厂工人,这与工厂模式无关,完全是无关紧要和令人困惑的。
为了解决上面的switch语句,您需要意识到一个明显的事实:每个条件处理程序都有某种相关性。在这种情况下,它们都是玩具。
public function createToy($toyName) {
    $toy = null;

    if ('car'==$toyName) {
        $toy = new NyCar();//I'm a Toy
    } else if ('helicopter'==$toyName) {
        $toy = new NyHelicopter();//I'm a Toy
    }

    return $toy;
}

使用多态,我们可以通过创建一组兄弟类来满足开闭原则:

abstract class Toy
+ operate()

Car extends Toy

Helicopter extends Toy

some_toy.operate();

在switch语句中添加内容只需要创建另一个同级类。

希望这可以帮助你!


谢谢 Rafael。我们来聊一聊工厂方法和简单工厂之间的区别。使用简单工厂可以完成所有操作,那么为什么还要使用工厂方法模式呢? - Nika Kurashvili
工厂方法是将实例化推迟到子类的一种模式。当您创建创建对象的接口但无法预测将创建哪些对象时,这非常重要。 - Rafael
我们可以在私聊中讨论吗?我已经学了很多关于这个模式,但还是有些困惑。我要问的问题会花费很多时间。提前感谢。 - Nika Kurashvili
@Rafael 如果工厂方法依赖于 DI 而不是像简单工厂中传递给开关的某些参数,那么它如何用作可以在运行时生成不同产品的工厂? 工厂方法是否会使用开关或某些控制逻辑来决定要实例化哪个产品或产品创建者? 如果这被嵌入到 DI 容器中,那么您将如何使该容器返回相同接口的不同类型实例? - Ε Г И І И О
@ΕГИІИО 是的,客户端需要控制逻辑来决定实例化哪个带有工厂方法的类,但是该控制逻辑绝对不应该在任何工厂方法中。那样会破坏设计模式的目的。 - Rafael
显示剩余6条评论

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