java 设计模式:工厂vs工厂方法vs抽象工厂

eagi6jfj  于 2023-04-04  发布在  Java
关注(0)|答案(9)|浏览(179)

我在一个网站上阅读设计模式
在那里我读到了关于工厂,工厂方法和抽象工厂的知识,但是它们太混乱了,我不清楚它们的定义。
工厂-创建对象而不向客户端公开示例化逻辑,并通过公共接口引用新创建的对象。* 是工厂方法的简化版本 *
工厂方法-定义了一个创建对象的接口,但让子类来决定示例化哪个类,并通过一个公共接口引用新创建的对象。
抽象工厂-提供了一个接口来创建一系列相关的对象,而不需要显式地指定它们的类。
我还看了其他关于抽象工厂与工厂方法的stackoverflow线程,但那里绘制的UML图使我的理解更糟。
谁能告诉我
1.这三种模式彼此之间有何不同?
1.何时使用哪种?
1.如果可能的话,还有任何与这些模式相关的Java示例吗?

x759pob2

x759pob21#

所有三种工厂类型都做同样的事情:他们是“聪明的建设者”。
假设您希望能够创建两种水果:苹果和橙子。

工厂

工厂是“固定的”,因为你只有一个没有子类的实现。在这种情况下,你将有一个像这样的类:

class FruitFactory {

  public Apple makeApple() {
    // Code for creating an Apple here.
  }

  public Orange makeOrange() {
    // Code for creating an orange here.
  }

}

用例:构造一个Apple或橙子有点太复杂,无法在构造器中处理。

工厂方法

工厂方法通常用于当你在一个类中有一些泛型处理,但想改变你实际使用的水果种类时。所以:

abstract class FruitPicker {

  protected abstract Fruit makeFruit();

  public void pickFruit() {
    private final Fruit f = makeFruit(); // The fruit we will work on..
    <bla bla bla>
  }
}

...然后你可以通过在子类中实现工厂方法来重用FruitPicker.pickFruit()中的公共功能:

class OrangePicker extends FruitPicker {

  @Override
  protected Fruit makeFruit() {
    return new Orange();
  }
}

抽象工厂

抽象工厂通常用于依赖注入/策略之类的事情,当您希望能够创建一整个家族的对象,这些对象需要是“相同类型”的,并且有一些公共的基类。这里有一个与水果相关的例子。这里的用例是我们要确保我们不会在Apple上意外使用OrangePicker。只要我们从同一个工厂获得水果和采摘器,它们就会匹配。

interface PlantFactory {
  
  Plant makePlant();

  Picker makePicker(); 

}

public class AppleFactory implements PlantFactory {
  Plant makePlant() {
    return new Apple();
  }

  Picker makePicker() {
    return new ApplePicker();
  }
}

public class OrangeFactory implements PlantFactory {
  Plant makePlant() {
    return new Orange();
  }

  Picker makePicker() {
    return new OrangePicker();
  }
}
disho6za

disho6za2#

1.这三种模式彼此之间有何不同?

**工厂:**创建对象,不向客户端暴露示例化逻辑。
**工厂方法:**定义一个创建对象的接口,但让子类决定示例化哪个类。
**抽象工厂:**提供创建相关对象族的接口,无需指定具体类。

  • AbstractFactory* 模式使用组合将创建对象的责任委托给另一个类
  • 工厂方法 * 设计模式使用继承并使用派生类来创建对象

1.何时使用哪种?

**工厂:**客户端只需要一个类,不需要实现者
**工厂方法:**客户端不知道创建对象需要哪些具体类
**AbstactFactory:**当您的系统需要创建多个系列的产品,而不暴露实现细节时。

抽象工厂类通常用工厂方法实现。工厂方法通常在模板方法中调用。
1.如果可能的话,还有任何与这些模式相关的Java示例吗?
代码片段:
工厂模式。何时使用工厂方法?

vybvopom

vybvopom3#

以下图片来自Design Patterns in C#, 2nd Edition by Vaskaran Sarcar图书:

1.简单工厂模式

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

SimpleFactory simpleFactory = new SimpleFactory();
IAnimal dog = simpleFactory.CreateDog(); // Create dog
IAnimal tiger = simpleFactory.CreateTiger(); // Create tiger

2.工厂方法模式

定义了一个创建对象的接口,但是让子类来决定示例化哪个类.

AnimalFactory dogFactory = new DogFactory(); 
IAnimal dog = dogFactory.CreateAnimal(); // Create dog

AnimalFactory tigerFactory = new TigerFactory();
IAnimal tiger = tigerFactory.CreateAnimal(); // Create tiger

3.抽象工厂模式(工厂的工厂)

Abstract Factory提供了一个接口,用于创建一系列相关对象,而无需显式指定它们的类

IAnimalFactory petAnimalFactory = FactoryProvider.GetAnimalFactory("pet");
IDog dog = petAnimalFactory.GetDog(); // Create pet dog
ITiger tiger = petAnimalFactory.GetTiger();  // Create pet tiger

IAnimalFactory wildAnimalFactory = FactoryProvider.GetAnimalFactory("wild");
IDog dog = wildAnimalFactory .GetDog(); // Create wild dog
ITiger tiger = wildAnimalFactory .GetTiger();  // Create wild tiger

mbzjlibv

mbzjlibv4#

Factory-分离Factory类创建复杂对象。

例如:FruitFactory类创建Fruit对象

class FruitFactory{

public static Fruit getFruit(){...}

}

工厂方法-不需要整个单独的类作为工厂,只需在该类中添加一个方法作为工厂。

例如:

Calendar.getInstance() (Java's Calendar)

抽象工厂-工厂的工厂

比如说我们想建一个电脑配件工厂,所以有几种类型的电脑,比如笔记本电脑,台式电脑,服务器。
因此,对于每种计算机类型,我们需要工厂。因此,我们创建一个工厂的高级工厂如下所示

ComputerTypeAbstractFactory.getComputerPartFactory(String computerType) ---> This will return PartFactory which can be one of these ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

现在这3个本身也是工厂。(您将处理PartFactory本身,但在幕后,将有基于您在抽象工厂中提供的单独实现)

Interface-> PartFactory. getComputerPart(String s), 
Implementations -> ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

Usage:
new ComputerTypeAbstractFactory().getFactory(“Laptop”).getComputerPart(“RAM”)

编辑:根据评论中的反对意见,编辑为Abstract Factory提供确切的接口。

ztigrdn8

ztigrdn85#

关于这个问题,我参考了“四人帮”的书。
书中没有**“工厂”、“简单工厂”和“虚拟工厂”的定义。通常当人们谈论“工厂”模式时,他们可能在谈论创建类的特定对象的东西(但不是“构建器”模式);他们可能会或可能不会指的是“工厂方法”或“抽象工厂”模式。任何人都可以实现“工厂”,因为他不会,因为它不是一个正式的术语(请记住,一些人\公司\社区可以有自己的词汇表)。
本书only包含“抽象工厂”和“工厂方法”的定义。
以下是书中的定义,并简要解释了为什么两者都如此令人困惑。我省略了代码示例,因为你可以在其他答案中找到它们:

工厂法(GOF):定义一个接口来创建一个对象,但是让子类来决定示例化哪个类。工厂方法让一个类将示例化推迟到子类。
抽象工厂(GOF):提供一个接口,用于创建相关或从属对象的族,而无需指定其具体类。
混淆来源:通常,我们可以将“工厂方法”模式中使用的类称为“工厂”。这个类根据定义是抽象的。这就是为什么很容易将这个类称为“抽象工厂”。但它只是类的名称;你不应该把它和“Abstract Factory”模式(class name!= pattern name)混淆。“Abstract Factory”模式是不同的--它不使用抽象类;它定义了一个接口(不一定是编程语言接口),用于创建一个或多个较大对象的部分,这些对象彼此相关或必须以特定方式创建。

rqcrx0a6

rqcrx0a66#

每一个设计模式都致力于确保编写好的工作代码不被触及。我们都知道,一旦我们触及了工作代码,现有的工作流程就会有缺陷,需要做更多的测试来确保我们没有破坏任何东西。
工厂模式基于输入条件创建对象,从而确保您不需要编写以下代码:

if (this) {
     create this kind of object 
 } else { 
     that kind of object 
 }

一个很好的例子是一个旅游网站。一个旅游网站只能提供旅游(航班,火车,巴士)或/和提供酒店或/和提供旅游景点套餐。现在,当用户选择下一步时,网站需要决定它需要创建什么对象。它应该只创建旅游或酒店对象吗?
现在,如果你设想在你的投资组合中添加另一个网站,并且你认为可以使用相同的核心,例如,一个拼车网站,现在可以搜索出租车并在线支付,你可以在你的核心中使用一个抽象工厂。这样你就可以再多一个出租车和拼车工厂。
两个工厂互不相干,所以把它们放在不同的工厂是一个很好的设计。
希望现在这是清楚的。研究网站再次记住这个例子,希望它会有所帮助。我真的希望我已经代表了正确的模式:)。

llycmphe

llycmphe7#

AbstractProductA, A1 and A2 both implementing the AbstractProductA
AbstractProductB, B1 and B2 both implementing the AbstractProductB

interface Factory {
    AbstractProductA getProductA(); //Factory Method - generate A1/A2
}

使用工厂方法,用户可以创建AbstractProductA的A1或A2。

interface AbstractFactory {
    AbstractProductA getProductA(); //Factory Method
    AbstractProductB getProductB(); //Factory Method
}

但抽象工厂有一个以上的工厂方法(例如:2个工厂方法),使用这些工厂方法,它将创建一组对象/相关对象。使用抽象工厂,用户可以创建AbstractProductA,AbstractProductB的A1,B1对象

xmd2e60i

xmd2e60i8#

没有人引用原书Design Patterns: Elements of Reusable Object-Oriented Software,它在“创造模式的讨论”一节的前两段给出了答案(重点是我的):
有两种常用的方法可以通过创建对象的类来参数化一个系统。一种方法是子类化创建对象的类;这对应于使用工厂方法(107)模式。这种方法的主要缺点是,它可能需要一个新的子类来更改产品的类。这种更改可能会级联。例如,当产品创建者本身由工厂方法创建时,您必须同时重写其创建者。
参数化系统的另一种方式更多地依赖于对象组合:定义一个对象,它负责知道产品对象的类,并使它成为系统的一个参数,这是抽象工厂的一个关键方面(87),建筑师(97),原型(117)模式。这三种模式都涉及到创建一个新的“工厂对象”,它的职责是创建产品对象。抽象工厂使工厂对象产生几个类的对象。Builder让工厂对象使用相应的复杂协议逐步构建复杂的产品。Prototype让工厂对象通过复制原型对象来构建产品。在这种情况下,工厂对象和原型是同一个对象,因为原型负责返回产品。

qvtsj1bj

qvtsj1bj9#

没有一个答案真正很好地解释了抽象工厂--可能是因为这个概念是抽象的,在实践中使用得不太频繁。
一个容易理解的例子来自考虑以下情况。
你有一个系统,它与另一个系统接口。我将使用Shalloway和Trott解释的设计模式,P194中给出的例子,因为这种情况非常罕见,我想不出更好的。在他们的书中,他们给出了具有本地硬件资源的不同组合的例子。他们使用,作为示例:

  • 具有高分辨率显示和打印驱动器的系统
  • 具有低分辨率显示器和打印驱动程序的系统

一个变量有两个选项(打印驱动程序,显示驱动程序),另一个变量有两个选项(高分辨率,低分辨率)。我们希望将这些变量耦合在一起,这样我们就有了一个HighResolutionFactory和一个LowResolutionFactory,它们为我们生成了正确类型的打印驱动程序和显示驱动程序。
这就是抽象工厂模式:

class ResourceFactory
{
    virtual AbstractPrintDriver getPrintDriver() = 0;

    virtual AbstractDisplayDriver getDisplayDriver() = 0;
};

class LowResFactory : public ResourceFactory
{
    AbstractPrintDriver getPrintDriver() override
    {
        return LowResPrintDriver;
    }

    AbstractDisplayDriver getDisplayDriver() override
    {
        return LowResDisplayDriver;
    }
};

class HighResFactory : public ResourceFactory
{
    AbstractPrintDriver getPrintDriver() override
    {
        return HighResPrintDriver;
    }

    AbstractDisplayDriver getDisplayDriver() override
    {
        return HighResDisplayDriver;
    }
};

我不会详细介绍打印驱动程序和显示驱动程序的层次结构,只需要一个就足够了。

class AbstractDisplayDriver
{
    virtual void draw() = 0;
};

class HighResDisplayDriver : public AbstractDisplayDriver
{
    void draw() override
    {
        // do hardware accelerated high res drawing
    }
};

class LowResDisplayDriver : public AbstractDisplayDriver
{
    void draw() override
    {
        // do software drawing, low resolution
    }
};

为什么它的工作原理:
我们可以用一堆if语句来解决这个问题:

const resource_type = LOW_RESOLUTION;

if(resource_type == LOW_RESOLUTION)
{
    drawLowResolution();
    printLowResolution();
}
else if(resource_type == HIGH_RESOLUTION)
{
    drawHighResolution();
    printHighResolution();
}

现在我们可以这样做:

auto factory = HighResFactory;
    auto printDriver = factory.getPrintDriver();
    printDriver.print();
    auto displayDriver = factory.getDisplayDriver();
    displayDriver.draw();

本质上-我们已经将运行时逻辑抽象到我们的类的v-table中。
我对这种模式的看法是它实际上并不是很有用。它将一些不需要耦合在一起的东西耦合在一起。设计模式的重点通常是减少耦合,而不是增加耦合,因此在某些方面,这种模式实际上是一种反模式,但在某些上下文中可能有用。
如果你到了一个阶段,你正在认真考虑实现这一点,你可能会考虑一些替代的设计。也许你可以写一个工厂,返回工厂,而工厂本身返回你最终想要的对象。我想这会更灵活,不会有同样的耦合问题。
增编:Gang of Four的例子也是类似的,它们有一个MotifFactory和一个PMFactory,然后产生PMWindowPMScrollBarMotifWindowMotifScrollBar分别。这是一个有点过时的文本现在,所以它可能很难理解的上下文。我记得我阅读这一章,我从这个例子中只理解了一个工厂基类的两个实现,它们返回不同的对象族。

相关问题