当前位置: 首页 > 知识库问答 >
问题:

创造者在工厂模式中的角色

花高爽
2023-03-14

我无法理解为factory类定义抽象类/接口的作用,这是我在web上所有教程中经常看到的。有人能解释一下创作者界面的重要性吗?工厂模式的参考UML图

以下是我的代码:

代码示例1

// Product
public abstract class Vehicle
{
     public string VehicleType { get; set; }
}

// Concrete Product
public class Bike : Vehicle
{
    public Bike()
    {
        VehicleType = "Two Wheeler";
    }
}

// Concrete Product
public class Car : Vehicle
{
    public Car()
    {
        VehicleType = "Four Wheeler";
    }
}

// Concrete Factory
public class VehicleFactory
{
     public Vehicle GetVehicle(string VehicleType)
    {
        if (VehicleType == "Bike")
            return new Bike();
        else if (VehicleType == "Car")
            return new Car();
        else
            return null;
    }
}

// Client class
public class ClientClass
{
    public void Main()
    {
        VehicleFactory VehicleFactoryObj = new VehicleFactory();
        Vehicle BikeObj = VehicleFactoryObj.GetVehicle("Bike");
        Vehicle CarObj = VehicleFactoryObj.GetVehicle("Car");
    }
}

上述代码不包含“VehicleFactory”类的任何抽象类。但效果很好。现在,为“VehicleFactory”添加抽象类的原因是什么?在我看来,添加抽象类对于抽象工厂方法是有意义的。[如果我错了,请纠正我]

GoF的定义:

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

据我所知,模式背后的核心问题是,您希望创建不同类的实例,而不向使用者公开创建逻辑。如果我有什么问题,请告诉我。我在这里也有点困惑,因为我在网上看到的例子。例如在Wiki上,Php和C#示例。我可以在C#示例中理解该模式的需求,但在PHP示例中不能。无论如何,下面的陈述将帮助你清楚地理解我的问题。

例如,我们的图书馆里有两个车辆类别Bike和Car,它们都有车辆型号。自行车型号以“BK”开头,汽车型号以“CR”开头。现在,我们希望根据车辆型号返回其中一个类的实例,而不向客户机公开逻辑。[注意,这是一个更新的场景,我之所以提出这个场景,是因为之前的一个场景在决定类的逻辑上很弱,并且在字符串的使用上造成了混乱]

因此,我们可以创建一个车辆工厂类,它公开了一个返回适当车辆实例的静态方法。

如果客户端知道选择逻辑,那么我可能不需要模式本身。因此,可能看起来像:

代码示例2

// Product
public abstract class Vehicle
{
     public int NumberOfWheels { get; set; }
}

// Concrete Product
html" target="_blank">public class Bike : Vehicle
{
    public Bike()
    {
        NumberOfWheels = 2;
    }
}

// Concrete Product
public class Car : Vehicle
{
    public Car()
    {
        NumberOfWheels = 4;
    }
}

// Client class
public class ClientClass
{
    public void Main()
    {
        String ModelNumber = "BK-125";

        Vehicle CurrentVehicle;
        if (ModelNumber.Contains("BK"))
        {
            CurrentVehicle = new Bike();
        }
        else if(ModelNumber.Contains("CR"))
        {
            CurrentVehicle = new Car();
        }
    }
}

工厂模式让我简单地通过创建工厂来隐藏客户端的创建逻辑。因此,客户端现在只需要调用工厂的创建方法,他将获得适当的类实例作为回报。现在代码看起来像。

代码示例3

// Product
public abstract class Vehicle
{
     public int NumberOfWheels { get; set; }
}

// Concrete Product
public class Bike : Vehicle
{
    public Bike()
    {
        NumberOfWheels = 2;
    }
}

// Concrete Product
public class Car : Vehicle
{
    public Car()
    {
        NumberOfWheels = 4;
    }
}

// Concrete Factory
public class VehicleFactory
{
     public Vehicle GetVehicle(string ModelNumber)
    {
        if (ModelNumber.Contains("BK"))
            return new Bike();
        else if (ModelNumber.Contains("CR"))
            return new Car();
        else
            return null;
    }
}

// Client class
public class ClientClass
{
    public void Main()
    {
        VehicleFactory VehicleFactoryObj = new VehicleFactory();
        Vehicle BikeObj = VehicleFactoryObj.GetVehicle("BK-125");
        Vehicle CarObj = VehicleFactoryObj.GetVehicle("CR-394");
    }
}

现在的问题是关于抽象工厂类
添加抽象工厂类的一个好处,我从讨论中了解到,客户机将能够重写“GetVehicle”方法来重写逻辑。例如,他可能创建了更多的车辆类别,例如“卡车”。但即使是在这种情况下,如果他想覆盖所有三个方面的工厂方法,即自行车、汽车和卡车,他也不会拥有完整的逻辑,因为自行车和汽车的创建逻辑是用工厂方法编写的。尽管他将能够为所有新车型创建新的逻辑。有人能帮我照一下吗?

我想说的更多一点是,这个问题是关于工厂模式的,我知道抽象工厂模式需要一个抽象工厂,因为在抽象工厂模式中,我们创建的是工厂工厂工厂。但在工厂模式中,我们只有一个对象工厂,那么为什么我们需要一个工厂接口呢?

提前感谢!!:-)

共有3个答案

洪鸿博
2023-03-14

简单地说,基类应该是抽象的,而不是抽象的,这取决于您是否想要声明一个新的基类。

如果您想阻止开发人员(或您自己)进行以下操作:

var myVehicle = new Vehicle();

而是强制所有车辆都应该是继承自车辆的子车辆,然后将其声明为抽象车辆。

然后,当他们尝试执行上述代码时,会出现编译错误,无法声明新的抽象类。

如果您对用户能够声明新基类感到满意,那么使用非抽象基类没有错。

糜宜民
2023-03-14

@Yair Halberstadt“...为什么我更喜欢工厂方法而不是静态工厂...”。两者有不同的用途。静态工厂是一个代码块,它(只是)收集对象层次结构的实例化代码(很容易相信这个名称)。但它的性质是静态的(尽管可以在实例级别编写),这意味着它在编译时被绑定。当然,我们可以扩展静态工厂,但主要是作为补丁代码。(在框架级别,客户端通常不喜欢扩展。)

相比之下,工厂方法对于编写常见的业务逻辑很有用,而无需指定客户将在以后编写的具体受益人类型。

一个虚构的例子(假设通用税收计算)-

public abstract class TaxComputer {
    protected abstract Calculator getCalculator();// Factory method
    public void registerIncome(){
        //...
    }
    public Amount computeTax(){
        Calculator alculator = getCalculator();
        //... Tax computation logic using abstract Calculator
        // Note: Real Calculator logic has not been written yet
        return tax;
    }
}

public interface Calculator {
    Amount add(Amount a, Amount b);
    Amount subtract(Amount a, Amount b);
    Amount multiply(Amount a, double b);
    Amount roundoff(int nearestAmount);
    // ...
}

我所有的税务规则实现都是指使用金额进行操作的抽象计算器。此时,它只需要抽象计算器。一旦税务计算机准备好了,就可以发布给客户机进行扩展(扩展点位于抽象类和钩子工厂方法)。

特定客户可以将其扩展到日元(不含小数点)或美元/英镑等的计算,甚至是根据当地规则在每次操作后四舍五入(例如,到下一个10卢比)的计算器。

同样,USDollarTaxCalculator将使用自己的操作规则对其进行扩展(但不能也不需要重新定义税务规则)

public class YenTaxComputer extends TaxComputer {
    @Override
    protected Calculator getCalculator() {
        return new YenCalculator();
    }
}

public class YenCalculator implements Calculator {
    @Override
    public Amount add(Amount a, Amount b) {
        /*Yen specific additions and rounding off*/ 
    }
    @Override
    public Amount subtract(Amount a, Amount b) {/*...*/ }
    @Override
    public Amount multiply(Amount a, double b) {/*...*/ }
    @Override
    public Amount roundoff(int nearestAmount) {/*...*/  }
}

在工厂中,方法点并不是隐藏创建逻辑,而是让客户机能够进行扩展。

客户看起来像

    public class TaxClient {
        public static void main(String[] args) {
            TaxComputer computer = new YenTaxComputer();//
            computeTax(computer);
        }
        /** A PLACE HOLDER METHOD FOR DEMO
         */
        private static void computeTax(TaxComputer computer) {
            Amount tax = computer.computeTax();
            Amount Payment = ...
            //...
        }
    }

这里需要注意的几点

  1. 客户端不使用工厂方法get计算器()
  2. Creator/工厂的具体实例可以通过静态工厂/反射等创建,可以从系统变量、平台、区域设置等中进行选择。
  3. 这种设计模式有其自身的缺点/成本,例如并行类层次结构(YenTaxComputer仅负责创建实例)。没有什么是免费的。
韦俊英
2023-03-14

是的,它确实有效,但并不理想VehicleFactory有一个非常通用的方法,一旦添加了更多车辆,使用这个方法将非常麻烦,因为需要一个非常长的方法来检查所有字符串。

想象一下你有15辆车。然后你需要一个非常长的方法来列举所有选项并生成正确的汽车。这不仅是不必要的慢,而且很容易出错,因为您可能很容易漏掉/删除某些内容,并且很难调试。一般来说,长方法是不好的代码气味。

此外,每次添加继承车辆的内容时,都需要编辑车辆工厂类。但是,如果您的库的用户没有访问权限,但希望从车辆继承,该怎么办?然而,通过定义抽象的VehicleFactory类,他可以从中继承并定义自己的工厂方法。

简而言之,抽象工厂方法只会使代码更易于扩展。

此外,根据字符串生成车辆也是一个非常糟糕的主意;如果你使用大写字母或拼写错误怎么办?除此之外,这是相当缓慢的。最好有这样的东西。

public abstract class VehicleFactory
{
     public abstract Vehicle GetVehicle(string VehicleType)
}

public class CarFactory : VehicleFactory
{
    public override Vehicle GetVehicle(string VehicleType)
    {
          return new Car();
    }
}

public class BikeFactory : VehicleFactory
{
    public override Vehicle GetVehicle(string VehicleType)
    {
          return new Bike();
    }
}

public class ClientClass
{
    public void Main()
    {
        //Create Factories
        BikeFactory BikeFactoryObj = new BikeFactory();
        CarFactory CarFactoryObj = new CarFactory();

        //create Vehicles from factories. If wanted they can be casted to the inherited type.
        Vehicle VehicleObj=BikeFactoryObj.GetNewVehicle();
        Bike BikeObj = (Bike)BikeFactoryObj.GetVehicle();
        Car CarObj = (Car)CarFactoryObj.GetVehicle();

        //They are all inherited from Vehicle so can be used in a list of Vehicles
        List<Vehicle> Vehicles=new List<Vehicle>()
        {
             VehicleObj,
             BikeObj,
             CarObj
        }
    }
}

这里发生错误的机会要少得多,而且这个类的任何用户都可以轻松地对其进行缩放。

 类似资料:
  • 主要内容:介绍,实现,Shape.java,Rectangle.java,Square.java,Circle.java,ShapeFactory.java,FactoryPatternDemo.java工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 介绍 意图:定义一个创建对象的接口,让其子

  • 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 介绍 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 主要解决:主要解决接口选择的问题。

  • 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 介绍 意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 主要解决:主要解决接口选择的问题。

  • 工厂模式是另外一种关注对象创建概念的创建模式。它的领域中同其它模式的不同之处在于它并没有明确要求我们使用一个构造器。取而代之,一个工厂能提供一个创建对象的公共接口,我们可以在其中指定我们希望被创建的工厂对象的类型。 试想一下,在我们被要求创建一种类型的UI组件时,我们就有一个UI工厂。并不是通过直接使用new操作符或者通过另外一个构造器来创建这个组件,我们取而代之的向一个工厂对象索要一个新的组件。

  • 创造模式处理一个对象的创建。创造模式的目的是为了在不按照约定而直接地创建的地方提供可选择的情况。 在工厂模式中,客户端查询一个对象而不知道这个对象来自哪里(即,哪一个类被用来生成它)。在一个工厂背后的思想是简化一个对象的创建。如果这个结果是通过一个中心函数来完成,相比之下要让一个客户端直接地使用类实例化来创建对象,跟踪哪一个对象被创建则会更容易些。通过分离要使用的代码,工厂减少了一个应用维护的复杂

  • 工厂模式 工厂模式是比较简单,也是比较好用的一种方式。根本上说,工厂模式的目的就根据不同的要求输出不同的产品。比如说吧,有一个生产鞋子的工厂,它能生产皮鞋,也能生产胶鞋。如果用代码设计,应该怎么做呢? typedef struct _Shoe { int type; void (*print_shoe)(struct _Shoe*); }Shoe; 就像上面说的,