虽然工厂方法模式的背后动机是允许子類別选择创建对象的具体類別,但是使用工厂方法模式也有一些其他的好处,其中很多并不依赖于子類別。因此,有时候也会创建不使用多态性创建对象的工厂方法,以得到使用工厂方法的其他好处。
工厂「方法」而非工厂「類別」
编辑
如果抛开设计模式的范畴,“工厂方法”这个词也可以指作为“工厂”的方法,这个方法的主要目的就是创建对象,而这个方法不一定在单独的工厂類別中。这些方法通常作为静态方法,定义在方法所实例化的類別中。
每个工厂方法都有特定的名称。在许多面向对象的编程语言中,构造方法必须和它所在的類別具有相同的名称,这样的话,如果有多种创建对象的方式(重载)就可能导致歧义。工厂方法没有这种限制,所以可以具有描述性的名称。举例来说,根据两个实数创建一个复数,而这两个实数表示直角坐标或极坐标,如果使用工厂方法,方法的含义就非常清晰了。当工厂方法起到这种消除歧义的作用时,构造方法常常被设置为私有方法,从而强制客户端代码使用工厂方法创建对象。
下面的例子展示了在不同的编程语言中实现复数创建的代码:
Java
编辑
class Complex {
public static Complex fromCartesianFactory(double real, double imaginary) {
return new Complex(real, imaginary);
}
public static Complex fromPolarFactory(double modulus, double angle) {
return new Complex(modulus * cos(angle), modulus * sin(angle));
}
private Complex(double a, double b) {
//...
}
}
Complex product = Complex.fromPolarFactory(1, pi);
VB.NET
编辑
Public Class Complex
Public Shared Function fromCartesianFactory(real As Double, imaginary As Double) As Complex
Return (New Complex(real, imaginary))
End Function
Public Shared Function fromPolarFactory(modulus As Double, angle As Double) As Complex
Return (New Complex(modulus * Math.Cos(angle), modulus * Math.Sin(angle)))
End Function
Private Sub New(a As Double, b As Double)
'...
End Sub
End Class
Complex product = Complex.fromPolarFactory(1, pi);
C#
编辑
public class Complex
{
public double Real;
public double Imaginary;
public static Complex FromCartesianFactory(double real, double imaginary)
{
return new Complex(real, imaginary);
}
public static Complex FromPolarFactory(double modulus, double angle)
{
return new Complex(modulus * Math.Cos(angle), modulus * Math.Sin(angle));
}
private Complex (double real, double imaginary)
{
Real = real;
Imaginary = imaginary;
}
}
var product = Complex.FromPolarFactory(1, pi);
简单工厂
编辑
普通的工厂方法模式通常伴随着对象的具体類別与工厂具体類別的一一对应,客户端代码根据需要选择合适的具体類別工厂使用。然而,这种选择可能包含复杂的逻辑。这时,可以创建一个单一的工厂類別,用以包含这种选择逻辑,根据参数的不同选择实现不同的具体对象。这个工厂類別不需要由每个具体产品实现一个自己的具体的工厂類別,所以可以将工厂方法设置为静态方法。
而且,工厂方法封装了对象的创建过程。如果创建过程非常复杂(比如依赖于配置文件或用户输入),工厂方法就非常有用了。
比如,一个程序要读取图像文件。程序支持多种图像格式,每种格式都有一个对应的ImageReader類別用来读取图像。程序每次读取图像时,需要基于文件信息创建合适類別的ImageReader。这个选择逻辑可以包装在一个简单工厂中:
public class ImageReaderFactory {
public static ImageReader imageReaderFactoryMethod(InputStream is) {
ImageReader product = null;
int imageType = determineImageType(is);
switch (imageType) {
case ImageReaderFactory.GIF:
product = new GifReader(is);
case ImageReaderFactory.JPEG:
product = new JpegReader(is);
//...
}
return product;
}
}