代码之家  ›  专栏  ›  技术社区  ›  Praveen Rai

创建者在工厂模式中的作用

  •  3
  • Praveen Rai  · 技术社区  · 7 年前

    我不理解为工厂类定义抽象类/接口的作用,这是我在网上所有教程中经常看到的。有人能解释一下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定义:

    定义用于创建对象的接口,但由子类决定 实例化哪个类。工厂方法允许类延迟 它用于子类的实例化。

    如果我这里有什么问题,请告诉我 Wiki

    例如,我们的图书馆里有两个汽车类——自行车和汽车,它们都有汽车型号。自行车型号以“BK”开头,汽车型号以“CR”开头。现在,我们希望根据车辆型号返回其中任何一个类的实例,而不向客户机公开逻辑。[ 笔记

    因此,我们可以创建一个vehicle factory类,该类公开了一个静态方法,该方法返回适当的vehicle实例。

    代码示例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();
            }
        }
    }
    

    // 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”方法来重写逻辑。对于他可能创建了更多车辆类的情况,例如“卡车”。但即使在这种情况下,如果他想要覆盖所有三个的工厂方法,即自行车、汽车和卡车,他也不会拥有完整的逻辑,因为自行车和汽车创建的逻辑是在工厂方法中编写的。尽管他将能够为所有新车型创建新的逻辑。谁能给这个点颜色看看吗?

    我想说的更多一点是 . 但在工厂模式中,我们只有一个对象工厂,那么为什么我们需要工厂的接口呢?

    提前感谢!!:-)

    4 回复  |  直到 7 年前
        1
  •  1
  •   Yair Halberstadt    7 年前

    是的,它很管用,但并不理想。 VehicleFactory

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

    这需要您编辑 车辆工厂 Vehicle . 但是,如果您的库的用户没有访问权限,但希望从车辆继承,会发生什么?然而,通过定义摘要 车辆工厂 类,并定义自己的工厂方法。

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

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

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

    这里出错的机会要少得多 该类的任何用户都可以轻松地对其进行扩展。

        2
  •  0
  •   Kedar Tokekar    7 年前

    相比之下,工厂方法在编写通用业务逻辑时非常有用,而无需指定具体受益人 哪些客户将在晚些时候写信。

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

    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. getCalculator()
    2. 创建者/工厂的具体实例可以通过静态工厂/反射等创建。可以从系统变量、平台、区域设置等方面进行选择。
        3
  •  0
  •   Steffen Cole Blake    7 年前

    如果您想阻止开发人员(或您自己)继续:

    var myVehicle = new Vehicle();
    

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

        4
  •  0
  •   Praveen Rai    7 年前

    我发现以下链接非常有助于理解为工厂创建抽象类背后的逻辑,以及使用工厂模式本身的逻辑。

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

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

    计算机汇编程序 直接分类 计算机工厂 类可以修改以创建新计算机类的实例(假设这个新类与计算机具有相同的接口)。此外,还可以通过创建一个新的工厂类(其接口与 计算机工厂 )这将创建新计算机类的实例(同样,具有与 ). 和以前一样,在 计算机汇编程序 类需要更改,所有的逻辑都像以前一样继续工作。这是将产品和工厂的类型抽象为不变接口的好处。

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