Creator 在工厂模式中的作用
Role of Creator in Factory Pattern
我无法理解为工厂 class 定义抽象 class / 接口的作用,这是我在网络上的所有教程中经常看到的内容。有人可以阐明 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' class 的任何摘要 class。但它工作正常。现在,为 'VehicleFactory' 添加摘要 class 的原因是什么?在我看来,添加一个抽象 class 对抽象工厂方法来说是有意义的。 [如有错误请指正]
更新:添加我对工厂模式的理解。
GoF 的定义:
Define an interface for creating an object, but let subclasses decide
which class to instantiate. The Factory method lets a class defer
instantiation it uses to subclasses.
据我所知,该模式背后的核心问题陈述是您想要创建不同 classes 的实例,而不向消费者公开创建逻辑。 如果我在这里有任何错误,请告诉我。由于我在网上看到的例子,我在这里也有点困惑。例如 Wiki、Php 和 C# 示例。我可以消化 C# 示例中模式的要求,但不能消化 PHP 示例中的模式。无论如何,下面的陈述将帮助您清楚地理解我的问题。
例如,我们的库中有两辆车 classes Bike 和 Car,它们都有车辆型号。自行车型号以 "BK" 开头,汽车型号以 "CR" 开头。现在,我们希望 return 根据车辆型号 classes 中的任何一个的实例,而不向客户端公开逻辑。 [注意这是一个更新的场景,我提出来是因为较早的场景在决定 class 时逻辑薄弱,并且对字符串的使用造成了混淆]
所以我们可以创建一个车辆工厂 class,它公开一个静态方法,returns 适当的车辆实例。
如果客户知道选择逻辑,那么我可能不需要模式本身。所以,可能看起来像:
代码示例 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();
}
}
}
工厂模式让我可以通过创建工厂来简单地向客户端隐藏创建逻辑。因此,Client 现在只需要调用 Factory 的 create 方法,他就会在 return 中获得适当的 class 实例。现在代码看起来像。
代码示例 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");
}
}
现在关于抽象工厂的问题来了class
添加抽象工厂 class 的一个好处是,我从讨论中了解到,客户端随后将能够覆盖 'GetVehicle' 方法来覆盖逻辑。对于他可能创造了更多车辆 class 的情况,例如 'Truck'。但即使在这种情况下,如果他想要重写所有三者(即 Bike、Car 和 Truck)的工厂方法,他也不会拥有完整的逻辑,因为创建 Bike 和 Car 的逻辑是用工厂方法编写的。尽管他将能够为他所有的新车辆类型创建新的逻辑。有人可以解释一下吗?
我想在这里说的更多一点是 这个问题是关于工厂模式的,我知道抽象工厂模式需要一个抽象工厂,因为在抽象工厂模式中我们正在创建工厂的工厂。但是在工厂模式中我们只有一个对象工厂,那么为什么我们需要一个工厂接口?
提前致谢!! :-)
是的,它有效,但并不理想。 VehicleFactory
有一个非常通用的方法,一旦添加更多车辆,使用起来就会很麻烦,因为您需要一个非常长的方法来检查所有字符串。
假设您有 15 辆车。然后你需要一个非常长的方法来枚举所有选项并生成正确的汽车。这既不必要地慢,又容易出错,因为你很容易错过一些东西 out/delete 它,而且很难调试。一般来说,长方法是不好的代码味道。
此外 这需要您在每次添加继承 Vehicle
. 的内容时编辑 VehicleFactory
class 但是如果您图书馆的用户无权访问它但想从车辆继承?然而,通过定义抽象 VehicleFactory
class 他可以继承并定义自己的工厂方法。
简而言之,抽象工厂方法只是让您的代码更易于扩展。
同时根据字符串生成车辆是一个非常糟糕的主意;如果您使用大写或拼写错误怎么办?除此之外,它的速度很慢。最好有这样的东西。
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
}
}
}
这里出现错误的机会要少得多,class 的任何用户都可以轻松解决这个问题。
简单来说,你的基础 classes 应该是抽象的还是不抽象的取决于你是否希望能够声明一个新的。
如果你想阻止开发者(或你自己)继续:
var myVehicle = new Vehicle();
而是强制所有 Vehicle 应该是从 Vehicle 继承的子 Vehicle,然后将其声明为抽象。
然后,当他们尝试执行上述代码时,他们会遇到无法声明新抽象的编译错误 class。
如果用户能够声明新的基础 class 是非抽象的,那么基础 class 没有任何问题。
@Yair Halberstadt“...为什么我更喜欢工厂方法而不是静态工厂...”。两者都有不同的用途。静态工厂是一个代码块,它(只是)收集对象层次结构的实例化代码(很容易相信这个名字)。但它的本质是静态的(虽然可以在实例级别编写)意味着它在编译时被绑定。当然我们可以扩展静态工厂,但主要是作为补丁代码。 (大多数情况下不喜欢在框架级别供客户扩展。)
相比之下,工厂方法对于编写通用业务逻辑很有用,无需指定具体受益人 types 客户将在以后编写。
一个虚构的例子(假设通用税收计算)-
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);
// ...
}
我所有的税收规则实施都引用了使用 Amount 进行操作的抽象计算器。此时只需要抽象计算器。一旦税务计算机准备就绪,它就可以发布供客户扩展(扩展点在抽象 classes 和挂钩工厂方法)。
特定客户可以将其扩展到日元(没有小数点)或美元/英镑等的计算,甚至是根据当地规则在每次操作后四舍五入(例如到下一个 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 = ...
//...
}
}
这里需要注意的地方
- 客户端不使用工厂方法 getCalculator()
- Creator/工厂的具体实例可以通过静态工厂/反射等创建。可以从系统变量、平台、区域设置等中进行选择
- 这种设计模式有其自身的缺点/成本,例如并行 class 层次结构(YenTaxComputer 仅负责创建实例)。没有什么是免费的。
我发现以下 link 非常有助于理解抽象 class 工厂背后的逻辑,以及使用工厂模式本身的逻辑。
https://msdn.microsoft.com/en-us/library/ee817667.aspx
让我引用文章中模式背后的逻辑
Consider an application that models the assembly of a variety of personal computers. The application contains a ComputerAssembler class that is responsible for the assembly of the computer, a Computer class that models the computer being built, and a ComputerFactory class that creates instances of the Computer class. In using the Factory pattern, the ComputerAssembler class delegates responsibility for creation of Computer instances to the ComputerFactory. This ensures that if the instantiation and/or initialization process changes (e.g. new constructor, use of an activator, custom pooling, etc.), the ComputerAssembler does not need to change at all. This is the benefit of abstracting the creation of an object from its use.
In addition, suppose that the business requirements changed and a new type of computer needs to be assembled. Rather than modifying the ComputerAssembler class directly, the ComputerFactory class can be modified to create instances of a new computer class (assuming that this new class has the same interface as Computer). Furthermore, it is also possible to address this requirement by creating a new factory class (that has the same interface as ComputerFactory) that creates instances of the new computer class (again, with the same interface as Computer). As before, nothing in the ComputerAssembler class needs to change, all the logic just continues to work as it had previously. This is the benefit of abstracting the types of the product and factory into invariant interfaces.
第二段指出 Creator 使我们能够创建新工厂,以处理产品中可能引入的更改,或者在他们的创建逻辑中,迫使我们创建新工厂。
我无法理解为工厂 class 定义抽象 class / 接口的作用,这是我在网络上的所有教程中经常看到的内容。有人可以阐明 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' class 的任何摘要 class。但它工作正常。现在,为 'VehicleFactory' 添加摘要 class 的原因是什么?在我看来,添加一个抽象 class 对抽象工厂方法来说是有意义的。 [如有错误请指正]
更新:添加我对工厂模式的理解。
GoF 的定义:
Define an interface for creating an object, but let subclasses decide which class to instantiate. The Factory method lets a class defer instantiation it uses to subclasses.
据我所知,该模式背后的核心问题陈述是您想要创建不同 classes 的实例,而不向消费者公开创建逻辑。 如果我在这里有任何错误,请告诉我。由于我在网上看到的例子,我在这里也有点困惑。例如 Wiki、Php 和 C# 示例。我可以消化 C# 示例中模式的要求,但不能消化 PHP 示例中的模式。无论如何,下面的陈述将帮助您清楚地理解我的问题。
例如,我们的库中有两辆车 classes Bike 和 Car,它们都有车辆型号。自行车型号以 "BK" 开头,汽车型号以 "CR" 开头。现在,我们希望 return 根据车辆型号 classes 中的任何一个的实例,而不向客户端公开逻辑。 [注意这是一个更新的场景,我提出来是因为较早的场景在决定 class 时逻辑薄弱,并且对字符串的使用造成了混淆]
所以我们可以创建一个车辆工厂 class,它公开一个静态方法,returns 适当的车辆实例。
如果客户知道选择逻辑,那么我可能不需要模式本身。所以,可能看起来像:
代码示例 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();
}
}
}
工厂模式让我可以通过创建工厂来简单地向客户端隐藏创建逻辑。因此,Client 现在只需要调用 Factory 的 create 方法,他就会在 return 中获得适当的 class 实例。现在代码看起来像。
代码示例 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");
}
}
现在关于抽象工厂的问题来了class
添加抽象工厂 class 的一个好处是,我从讨论中了解到,客户端随后将能够覆盖 'GetVehicle' 方法来覆盖逻辑。对于他可能创造了更多车辆 class 的情况,例如 'Truck'。但即使在这种情况下,如果他想要重写所有三者(即 Bike、Car 和 Truck)的工厂方法,他也不会拥有完整的逻辑,因为创建 Bike 和 Car 的逻辑是用工厂方法编写的。尽管他将能够为他所有的新车辆类型创建新的逻辑。有人可以解释一下吗?
我想在这里说的更多一点是 这个问题是关于工厂模式的,我知道抽象工厂模式需要一个抽象工厂,因为在抽象工厂模式中我们正在创建工厂的工厂。但是在工厂模式中我们只有一个对象工厂,那么为什么我们需要一个工厂接口?
提前致谢!! :-)
是的,它有效,但并不理想。 VehicleFactory
有一个非常通用的方法,一旦添加更多车辆,使用起来就会很麻烦,因为您需要一个非常长的方法来检查所有字符串。
假设您有 15 辆车。然后你需要一个非常长的方法来枚举所有选项并生成正确的汽车。这既不必要地慢,又容易出错,因为你很容易错过一些东西 out/delete 它,而且很难调试。一般来说,长方法是不好的代码味道。
此外 这需要您在每次添加继承 Vehicle
. 的内容时编辑 VehicleFactory
class 但是如果您图书馆的用户无权访问它但想从车辆继承?然而,通过定义抽象 VehicleFactory
class 他可以继承并定义自己的工厂方法。
简而言之,抽象工厂方法只是让您的代码更易于扩展。
同时根据字符串生成车辆是一个非常糟糕的主意;如果您使用大写或拼写错误怎么办?除此之外,它的速度很慢。最好有这样的东西。
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
}
}
}
这里出现错误的机会要少得多,class 的任何用户都可以轻松解决这个问题。
简单来说,你的基础 classes 应该是抽象的还是不抽象的取决于你是否希望能够声明一个新的。
如果你想阻止开发者(或你自己)继续:
var myVehicle = new Vehicle();
而是强制所有 Vehicle 应该是从 Vehicle 继承的子 Vehicle,然后将其声明为抽象。
然后,当他们尝试执行上述代码时,他们会遇到无法声明新抽象的编译错误 class。
如果用户能够声明新的基础 class 是非抽象的,那么基础 class 没有任何问题。
@Yair Halberstadt“...为什么我更喜欢工厂方法而不是静态工厂...”。两者都有不同的用途。静态工厂是一个代码块,它(只是)收集对象层次结构的实例化代码(很容易相信这个名字)。但它的本质是静态的(虽然可以在实例级别编写)意味着它在编译时被绑定。当然我们可以扩展静态工厂,但主要是作为补丁代码。 (大多数情况下不喜欢在框架级别供客户扩展。)
相比之下,工厂方法对于编写通用业务逻辑很有用,无需指定具体受益人 types 客户将在以后编写。
一个虚构的例子(假设通用税收计算)-
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);
// ...
}
我所有的税收规则实施都引用了使用 Amount 进行操作的抽象计算器。此时只需要抽象计算器。一旦税务计算机准备就绪,它就可以发布供客户扩展(扩展点在抽象 classes 和挂钩工厂方法)。
特定客户可以将其扩展到日元(没有小数点)或美元/英镑等的计算,甚至是根据当地规则在每次操作后四舍五入(例如到下一个 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 = ...
//...
}
}
这里需要注意的地方
- 客户端不使用工厂方法 getCalculator()
- Creator/工厂的具体实例可以通过静态工厂/反射等创建。可以从系统变量、平台、区域设置等中进行选择
- 这种设计模式有其自身的缺点/成本,例如并行 class 层次结构(YenTaxComputer 仅负责创建实例)。没有什么是免费的。
我发现以下 link 非常有助于理解抽象 class 工厂背后的逻辑,以及使用工厂模式本身的逻辑。
https://msdn.microsoft.com/en-us/library/ee817667.aspx
让我引用文章中模式背后的逻辑
Consider an application that models the assembly of a variety of personal computers. The application contains a ComputerAssembler class that is responsible for the assembly of the computer, a Computer class that models the computer being built, and a ComputerFactory class that creates instances of the Computer class. In using the Factory pattern, the ComputerAssembler class delegates responsibility for creation of Computer instances to the ComputerFactory. This ensures that if the instantiation and/or initialization process changes (e.g. new constructor, use of an activator, custom pooling, etc.), the ComputerAssembler does not need to change at all. This is the benefit of abstracting the creation of an object from its use.
In addition, suppose that the business requirements changed and a new type of computer needs to be assembled. Rather than modifying the ComputerAssembler class directly, the ComputerFactory class can be modified to create instances of a new computer class (assuming that this new class has the same interface as Computer). Furthermore, it is also possible to address this requirement by creating a new factory class (that has the same interface as ComputerFactory) that creates instances of the new computer class (again, with the same interface as Computer). As before, nothing in the ComputerAssembler class needs to change, all the logic just continues to work as it had previously. This is the benefit of abstracting the types of the product and factory into invariant interfaces.
第二段指出 Creator 使我们能够创建新工厂,以处理产品中可能引入的更改,或者在他们的创建逻辑中,迫使我们创建新工厂。