造物主在工厂模式中的角色

时间:2017-09-26 18:32:26

标签: c# design-patterns factory-pattern

我无法理解为工厂类定义抽象类/接口的作用,这是我在Web上的所有教程中总能看到的。有人可以说一下CreatorInterface的重要性吗? Reference UML Diagram of the Factory Pattern

要输入代码形式,这就是我所拥有的:

代码示例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的定义:

  

定义用于创建对象的接口,但让子类决定   要实例化的类。 Factory方法允许类延迟   它用于子类的实例化。

据我所知,模式背后的核心问题是你想要创建不同类的实例,而不是将创建逻辑暴露给消费者。 如果我在这里弄错了,请告诉我。我在这里也有点困惑,因为我在网上看到的例子。例如,在Wiki上,Php和C#示例。我可以在C#示例中消化模式的要求,但不能在PHP示例中消化。无论如何,以下陈述将帮助您清楚地理解我的问题。

例如,我们的图书馆有两个车辆类Bike和Car,它们都有车型号。 Bike型号以“BK”开头,车型号以“CR”开头。现在,我们希望根据Vehicle Model Number返回任一类的实例,而不将逻辑暴露给客户端。 [注意这是一个更新的场景,我提出,因为前面的一个决定了这个类的逻辑很弱,并且对使用字符串产生了混淆]

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

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

代码示例2

// 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;
    }
}

// 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();
        }
    }
}

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

代码示例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'方法来覆盖逻辑。对于他可能创造了更多车辆类别的情况,例如“卡车”。但即使在这种情况下,如果他想要覆盖所有三种工厂方法,即自行车,汽车和卡车,他将不会有他的整个逻辑,因为自行车和汽车创建的逻辑是用Factory方法编写的。虽然他将能够为他所有的新车型创造新的逻辑。有人可以对此有所了解吗?

我更想指出的是这个问题是关于工厂模式,我明白抽象工厂模式将需要一个抽象工厂,因为在抽象工厂模式中我们正在创建工厂工厂< / strong>即可。但是在Factory模式中我们只有一个对象工厂,那为什么我们需要工厂的接口呢?

提前致谢!! : - )

4 个答案:

答案 0 :(得分:0)

是的,它有效,但它并不理想。 VehicleFactory有一个非常通用的方法,一旦你添加更多的车辆,将会很痛苦,因为你需要一个非常长的方法来检查所有的字符串。

想象一下,你有十五辆车。然后,您需要一个非常长的方法来枚举所有选项并生成正确的汽车。这都是不必要的慢,并且容易出错,因为你很容易错过/删除它,并且调试起来会非常困难。一般来说长方法都有不良的代码味道。

除了之外,每次添加继承VehicleFactory的内容时都需要编辑Vehicle类。但是如果您的图书馆用户没有这样做,会发生什么?可以访问它但想继承车辆吗?但是,通过定义一个抽象的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
        }
    }
}

此处出现错误的机会要少得多,而且这可以轻松地由班级中的任何用户进行缩放。

答案 1 :(得分:0)

简单来说,您的基类应该是抽象的,取决于您是否希望能够声明一个新类。

如果你想阻止开发者(或你自己)前往:

var myVehicle = new Vehicle();

而是强制所有车辆应该是从车辆继承的孩子,然后你宣布它是抽象的。

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

如果用户可以声明新的基类,那么基类非抽象没有错。

答案 2 :(得分:0)

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

相比之下,Factory方法对于编写通用业务逻辑非常有用,无需指定客户将在以后日期编写的具体受益人类型

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

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);
    // ...
}

我的所有Tax规则实现都是指使用 Amount 进行操作的抽象计算器。它目前只需要抽象计算器。 Tax计算机准备就绪后,可以发布它以供客户端扩展(扩展点为抽象类和钩子工厂方法)。

特定客户可以将其扩展到Yen(没有小数点)或Dollar / Pound等的计算,甚至可以根据当地规则在每次操作后将计算器(例如下一个10卢比)四舍五入。

相同的方式USDollarTaxCalculator将使用自己的操作规则扩展它(但不能也不需要重新定义Tax规则)

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. 客户端不使用工厂方法 getCalculator()
  2. 可以通过静态工厂/反射等创建具体的创建者/工厂实例。可以从系统变量,平台,区域设置等中进行选择。
  3. 这种设计模式有其自身的缺点/成本,如并行类层次结构(YenTaxComputer只负责创建实例)。什么都没有。

答案 3 :(得分:0)

我发现以下链接非常有助于理解为Factory提供抽象类的逻辑,以及使用Factory Pattern本身的逻辑。

https://msdn.microsoft.com/en-us/library/ee817667.aspx

让我引用文章背后的逻辑

  

考虑一个为各种个人计算机的装配建模的应用程序。该应用程序包含一个负责组装计算机的 ComputerAssembler 类,一个为正在构建的计算机建模的计算机类,以及一个 ComputerFactory 创建Computer类实例的类。在使用工厂模式时, ComputerAssembler 类将创建计算机实例的责任委派给 ComputerFactory 。这确保了如果实例化和/或初始化过程发生变化(例如,新构造函数,激活器的使用,自定义池等), ComputerAssembler 根本不需要改变。这是从使用中抽象对象的创建的好处。

     

此外,假设业务需求发生变化,需要组装新型计算机。可以修改 ComputerFactory 类来创建新计算机类的实例(假设此新类具有与计算机相同的接口),而不是直接修改 ComputerAssembler 类。此外,还可以通过创建一个新工厂类(与 ComputerFactory 具有相同的接口)来创建新计算机类的实例(同样具有与计算机)。和以前一样, ComputerAssembler 类中的任何内容都不需要更改,所有逻辑都会像以前一样继续工作。这是将产品和工厂的类型抽象为不变接口的好处。

第二段指出,创造者使我们能够创建新工厂,处理产品中可能引入的变化,或者创建它们的逻辑,迫使我们创建新工厂。