设计模式:工厂模式
工厂模式是重要的创建型模式之一,所有的开发者都应当了解,它是许多高级设计模式的基础。长时间以来,我一直弄不清不同类型的工厂模式的区别,另外,很难找到一篇能将清除这些模式的文章。本文主要叙述以下工厂模式的以下几点:
- 工厂方法模式
- 抽象工厂模式
- 静态工厂方法
- 简单工厂
工厂方法模式在“Gang of Four”的《设计模式》一书中提到,我第一次在书中读到该内容时,我将它与 Joshua Bloch 在《Effective Java》中提到的静态工厂弄混了。简单工厂(也就是常说的工厂模式)是常规的也是最常用的一个。至于抽象工厂,他在“Gang of Four”的书中,作为工厂方法模式的拓展被提出。
本文中,我将解释工厂模式为什么有用,并且针对每种类型,我都会给出从著名的Java框架或者接口中选取的示例。这里将使用Java语言去实现工厂模式,不过不了解Java也不妨碍你理解这些概念。另外,我将使用UML来描述工厂模式。
反设计模式
虽然本文是关于工厂模式的,但是必须指出的一点是,为了使用设计模式而使用设计模式,不如不使用设计模式。这就是反设计模式,事实上,过多的设计模式会使得代码更难懂。大多数时候,我并不使用工厂模式。例如:
- 当我独自编写代码时,我会避免使用工厂模式。
- 对于不会有大的改动的小项目,我会避免使用工厂模式。
- 但在多人参与的中大型项目中,工厂模式是很有用的。
我始终认为,工厂模式是需要在它的易用性以及代码的可读性之间做权衡的。
工厂的主要作用是实例化对象,那为什么不直接调用构造方法构造对象呢?
对于简单的用例,确实没有必要使用工厂。我们来看下面这个例子:
public class SimpleClass {
private final Integer arg1;
private final Integer arg2;
SimpleClass(Integer arg1, Integer arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
}
public Integer getArg1(){
return arg1;
}
public Integer getArg2(){
return args;
}
}
...
public class BusinessClassXYZ {
public static void someFunction(){
SimpleClass mySimpleClass = new SimpleClass(1,2);
// some stuff
}
}
在这个例子中,SimpleClass
是一个非常简单的类,只有两个状态变量,没有多态和业务逻辑。你可以使用工厂模式去创建该类的对象,然而这样做会是代码量加倍,同时也使得代码更难理解。如果你可以避免使用工厂模式,代码必将更佳简洁。
但是,在实现较大的应用时,你会遇到更加复杂的情况,参与的开发者和代码修改会更多,对于这些复杂情况,工厂模式是利大于弊的。
工厂的必要性
在说明了工厂模式的弊端之后,我们来看看工厂模式的被广泛应用的原因。
实例控制
企业级应用中,通常需要限制一个类的实例数量,因为实例可能会消耗资源(socket、数据库连接或者文件描述符等),应当怎样控制一个类只有一个实例(或者两个、10个)呢?
倘若使用构造方法来创建,在一个函数中(或者类中)是很难知道是否已经有该类的实例存在的,另外,即便已经有一个该类的实例存在,该函数应该怎样去获取这个实例呢?你可以使用每个函数都可以获取到的共享变量,但是
- 这样会链接所有的需要该类的实例的函数行为,因为他们使用并修改同一个共享变量。
- 许多地方会有同样的检测该类是否已经实例化的代码逻辑,这样会有很严重的代码重复。
使用静态工厂方法,你可以很容易解决这个问题:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
...
}
...
public class ClassXXX{
...
public static void someFunctionInClassXXX(){
Singleton instance = Singleton.getInstance();
//some stuff
}
}
...
public class ClassYYY{
...
public static void someFunctionInClassYYY(){
Singleton instance = Singleton.getInstance();
//some stuff
}
}
在以上的代码中,我们使用工厂模式,将Singleton
类的实例个数限制为1。在限制对象的个数时,我们创建了一个实例池,这种池模式是基于工厂模式的。
注意:我们可以修改创建实例的方法,而不是限制实例的个数(例如,使用原型模式,而不是每次都从头创建对象)。
松耦合
工厂的另一个优点就是可以松耦合。
假设你写了一个程序去计算数据并打印日志,因为这是一个大项目,在你写业务代码的同事,你的同事编写了一个类来将日志存入文件系统(FileSystemLogger
类)。不使用工厂模式的情况下,你需要先调用构造方法来实例化FileSystemLogger
类,然后才能使用它:
public class FileSystemLogger {
...
public void writeLog(String s) {
//Implemation
}
}
...
public void someFunctionInClassXXX(some parameters){
FileSystemLogger logger= new FileSystemLogger(some paramters);
logger.writeLog("This is a log");
}
然而,突然有一个新的需求,你需要使用已经实现好的DatabaseLogger
类将日志写到数据库中,你将怎么办呢?倘若不使用工厂,你需要修改所有的使用了FileSystemLogger
类的函数,因为日志打印在很多地方都会用到,你可能需要修改几百个函数和类。如果使用工厂,你就可以很容易的从一种实现转换到另一个,只需要修改工厂。
//this is an abstraction of a Logger
public interface ILogger {
public void writeLog(String s);
}
public class FileSystemLogger implements ILogger {
...
public void writeLog(String s) {
//Implemation
}
}
public class DatabaseLogger implements ILogger {
...
public void writeLog(String s) {
//Implemation
}
}
public class FactoryLogger {
public static ILogger createLogger() {
//you can choose the logger you want
// as long as it's an ILogger
return new FileSystemLogger();
}
}
some code using the factory
public class SomeClass {
public void someFunction() {
//if the logger implementation changes
//you have nothing to change in this code
ILogger logger = FactoryLogger.createLogger();
logger.writeLog("This is a log");
}
}
使用上面的代码,你可以很容易将FileSystemLogger
改为DatabaseLogger
,只需要修改createLogger()
函数就可以了(该函数就是工厂)。该修改对客户端(业务代码)是不可见的,因为客户代码使用的是日志接口(ILogger
),而日志类的具体实现
由工厂完成。这样以来,你就在日志类的实现和业务代码直接创建了松耦合。
封装
有时候,使用工厂可以提高代码的可读性,降低封装的复杂度。
假设你要使用一个业务类CarComparator
去比较两辆车,该类需要一个数据库连接(DatabaseConnection
)以获取各种车的特征数据,还需要从配置文件(FileSystemConnection
)中去获取比较算法需要的参数(例如:增加燃油消耗的权重,使其比最大速度的权重大)。
不使用工厂,你可能会写出以下代码:
public class DatabaseConnection {
DatabaseConnection(some parameters) {
// some stuff
}
...
}
public class FileSystemConnection {
FileSystemConnection(some parameters) {
// some stuff
}
...
}
public class CarComparator {
CarComparator(DatabaseConnection dbConn, FileSystemConnection fsConn) {
// some stuff
}
public int compare(String car1, String car2) {
// some stuff with objets dbConn and fsConn
}
}
...
public class CarBusinessXY {
public void someFunctionInTheCodeThatNeedsToCompareCars() {
DatabaseConnection db = new DatabaseConnection(some parameters);
FileSystemConnection fs = new FileSystemConnection(some parameters);
CarComparator carComparator = new CarComparator(db, fs);
carComparator.compare("Ford Mustang","Ferrari F40");
}
...
}
public class CarBusinessZY {
public void someOtherFunctionInTheCodeThatNeedsToCompareCars() {
DatabaseConnection db = new DatabaseConnection(some parameters);
FileSystemConnection fs = new FileSystemConnection(some parameters);
CarComparator carComparator = new CarComparator(db, fs);
carComparator.compare("chevrolet camaro 2015","lamborghini diablo");
}
...
}
以上代码可以实现功能,但是,为了实现比较的功能,你需要以下实例:
DatabaseConnection
FileSystemConnection
CarComparator
如果你想在更多的函数里使用比较功能,你需要复杂你的代码,一旦CarComparation
类的构造发生改变,你需要修改所有复制的代码。使用工厂可以分解代码,隐藏CarComparator
类的构造方法的复杂性。
...
public class Factory {
public static CarComparator getCarComparator() {
DatabaseConnection db = new DatabaseConnection(some parameters);
FileSystemConnection fs = new FileSystemConnection(some parameters);
CarComparator carComparator = new CarComparator(db, fs);
}
}
//some code using the factory
public class CarBusinessXY {
public void someFunctionInTheCodeThatNeedsToCompareCars() {
CarComparator carComparator = Factory.getCarComparator();
carComparator.compare("Ford Mustang","Ferrari F40");
}
...
}
...
public class CarBusinessZY {
public void someOtherFunctionInTheCodeThatNeedsToCompareCars() {
CarComparator carComparator = Factory.getCarComparator();
carComparator.compare("chevrolet camaro 2015","lamborghini diablo");
}
...
}
比较两段代码,你可以看到,使用工厂:
- 减少了代码的行数
- 避免了代码重复
- 组织代码:工厂负责构建
CarComparator
,业务代码只是使用它
最后一点至关重要(事实上都很重要),因为这样有助于关注点分离,业务类不需要知道怎样去构造一个复杂的对象,它只需要使用它,业务类只需要关注业务逻辑即可。另外,这样也有助于同项目中的开发者分工:
- 一个人编写
CarComparator
及其创建方法 - 其他人实现业务类,使用
CatComparator
实例
消除歧义
假设你写了一个有很多构造方法的类(行为各不相同),你应该怎么样保证不会误用构造方法呢?
我们来看以下代码:
class Example{
//constructor one
public Example(double a, float b) {
//...
}
//constructor two
public Example(double a) {
//...
}
//constructor three
public Example(float a, double b) {
//...
}
}
前两个构造方法的参数数量不同,你可以很快确定使用哪个,尤其当你使用 IDE 的代码提示时,但是选择第一个构造方法和第三个构造方法,就会难很多。这个例子可能看起来不太真实,但确实是我曾经经历的真实故事。
那么,怎么样才能实现参数相同的不同构造方法呢(同时避免以上代码中的那种实现)?
这里给出使用工厂的实现:
class Complex {
public static Complex fromCartesian(double real, double imag) {
return new Complex(real, imag);
}
public static Complex fromPolar(double rho, double theta) {
return new Complex(rho * Math.cos(theta), rho * Math.sin(theta));
}
private Complex(double a, double b) {
//...
}
}
在这个例子中,使用工厂添加了对构造方法的描述,也就是工厂函数名:你可以使用笛卡尔坐标或者极坐标去构造一个复数,在两种构造方法中,你都能准确知道构造函数的作用。
工厂模式
在了解了工厂模式的利弊之后,让我们来着重看看不同类型的工厂模式。
我将由简单到抽象逐个讲解每一种工厂,如果你要使用工厂模式,请记住,越简单越好。
静态工厂方法
注意:如果你没有学过 Java,你可以将静态方法理解成类方法。
Joshua Bloch 在 “Effective Java” 中是这样描述静态工厂方法的:
“A class can provide a public static factory method, which is simply a static method that returns an instance of the class.”
换句话说,除了使用构造方法去创建一个实例,类也可以提供一个静态方法,用于返回该类的实例。如果该类有子类,那么该静态方法可以该会该类或者子类的实例。尽管我不喜欢 UML,但是正如文章开头所说,在这里我使用 UML 给出正式的描述:
在上图中,ObjectWithStaticFactory
类有一个静态工厂方法(getObject()
),该方法可以生成ObjectWithStaticFactory
类及其子类的实例。该类也还可以有其他的方法、变量或者静态方法。
让我们看看下面的代码:
public class MyClass {
Integer a;
Integer b;
MyClass(int a, int b){
this.a=a;
this.b=b;
};
public static MyClass getInstance(int a, int b){
return new MyClass(a, b);
}
public static void main(String[] args){
//instanciation with a constructor
MyClass a = new MyClass(1, 2);
//instanciation with a static factory method
MyClass b = MyClass.getInstance(1, 2);
}
}
这段代码展示了两种创建MyClass
类实例的方法:
- 静态工厂方法
getInstance()
- 构造方法
这里可以在深究一下,如果一个类的静态工厂方法可以返回其他类的实例呢?Joshua Bloch 描述了这种情况:
“Interfaces can’t have static methods, so by convention, static factory methods for an interface named Type are put in a noninstantiable class (Item 4) named Types.”
下面给出以上情况的 UML:
在这种情况中,工厂方法getObject()
在Types
抽象类中,该方法可以创建Type
类及其子类的实例,getObject()
方法可以有参数,所以可以由参数决定返回SubType1
或者SubType2
的实例。
让我们回到 Java 中,假设我们有两个类:Ferrari
和Mustang
,都实现了Car
接口,静态工厂方法可以被放在抽象类CarFactory
中(按照 Joshua Bloch 的习惯,该类应该被命名为Cars
,但是我不喜欢这种命名方式):
/the products
public interface Car {
public void drive();
}
public class Mustang implements Car{
public void drive() {
// some stuff
}
...
}
public class Ferrari implements Car{
public void drive() {
// some stuff
}
...
}
/ the factory
public abstract class CarFactory{
public static Car getCar() {
// choose which car you want
}
}
...
/some code using the factory
public static void someFunctionInTheCode(){
Car myCar = CarFactory.getCar();
myCar.drive();
}
与其他的工厂模式相比,这种模式的好处在于,你不需要:
- 在使用工厂时,先实例化工厂类
- 工厂类实现接口
这种模式简单易用,但是只能在提供了类方法的语言中使用(例如 Java 中的 static 关键字)。
注意:当提到工厂时,网上许多文章都弄错了,例如stackoverflow上的这篇文章,尽管被点赞了1500次。问题在于他们给出的关于工厂方法模式的例子实际上都是静态工厂方法,正如 Joshua Bloch 所说的:
“a static factory method is not the same as the Factory Method pattern from Design Patterns [Gamma95, p. 107]. The static factory method described in this item has no direct equivalent in Design Patterns.”
如果你去看 stackoverflow 上的文章,只有上面给出的例子是 GoF 定义的工厂方法模式。
实例
这里给出 Java API 和框架中关于静态工厂方法的例子,在 Java API 中招相关的例子非常容易,因为 Joshua Bloch 本人就是许多 Java API 的架构师。
日志框架
Java 日志框架 slf4j, logback, log4j 都使用了抽象类LoggerFactory
,如果开发者想写日志,他需要通过LoggerFactory
类中的静态方法getLogger()
获取Logger
的实例。
getLogger()
方法返回的Logger
的具体内容取决于该方法的具体实现(也包括开发者使用的getLogger()
方法发配置文件)。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Example{
public void example() {
//we're using the static factory method to get our logger
Logger logger = LoggerFactory.getLogger(Example.class);
logger.info("This is an example.");
}
}
注意:使用 slf4j 或者 log4j 时,工厂类的类名和它的静态工厂方法的方法名是不尽相同的。
Java String 类
Java 中的String
类用来表示字符串。有时你需要将整数或者布尔类型转化为字符串,但是String
类并没有提供类似于String(Integer i)
或者String(Boolean b)
的构造方法,而是提供了一些静态方法String.value(Of)
。
int i = 12;
String integerAsString = String.valueOf(i);
简单工厂
这个模式并不是一个正式的设计模式,但是我多次在网上看到这一说法,它并没有正式的定义,这里给出我自己的描述:简单工厂是一个工具
- 它的主要作用是实例化对象
- 并非是工厂方法模式
- 也不是抽象工厂模式
你可以看到,它是静态工厂模式的泛化,这里的工厂可能可以实例化,也可能不能实例化,因为工厂方法不再是一个类方法(也可能是)。对于一个 Java 开发者,使用非静态形式的简单工厂的机会很少。所以,大多数时候,简单工厂就等于静态工厂。这里给出非静态工厂形式的 UML 图:
这里,工厂方法getObject()
在Factory
类中,工厂方法并不是类方法,所以你需要实例化工厂,然后才能使用它,工厂方法同样也可以创建Type
类及其子类的实例。
这里给出之前静态工厂方法的例子,但是现在在使用之前会先实例化工厂。
/the products
public interface Car {
public void drive();
}
public class Mustang implements Car{
public void drive() {
// some stuff
}
...
}
public class Ferrari implements Car{
public void drive() {
// some stuff
}
...
}
/The factory
public class CarFactory{
//this class is instantiable
public CarFactory(){
//some stuff
}
public Car getCar() {
// choose which car you want
}
}
...
/some code using the factory
public static void someFunctionInTheCode(){
CarFactory carFactory = new CarFactory();
Car myCar = carFactory.getCar();
myCar.drive();
}
如你所见,这里我们需要实例化Factory
,然后才能使用它。我没有找到简单工厂的相关实例,因为使用静态工厂方法可能是更好的选择。不过,如果你需要工厂的实例的话, 你也可以使用非静态形式的工厂。例如,你需要一个数据库连接,你可以先实例化你的工厂(同时会实例化数据库连接),然后再使用需要此连接的工厂方法。对我个人而言,这种情况下,我还是会使用讲台工厂,并使用懒加载(或者数据库连接池)。
如果你知道哪个 Java 框架使用了非静态形式的简单工厂,请告知我。
工厂方法模式
工厂方法模式是过呢国家抽象的工厂,这里给出"Gang of Four"关于该模式的定义:
“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses”
下面是工厂方法方法模式的 UML 图:
上图和非静态形式的简单工厂有一些相似,唯一的(也是最大的)区别就在于Factory
接口:
Factory
表示了创建对象的接口,它描述了一个工厂方法:getObjects()
ConcreteFactory
表示了子类决定实例化哪个类,每一个ConcreteFactory
类都有它自己的工厂方法的实现getObject()
。
在上图中,getObjects()
需要返回一个Type
类(或者它的子类)的实例,也就是说,每一个具体的工厂都可以返回不同的Type
类的子类的实例。
为什么要使用工厂方法模式而不是简单工厂呢?
当你的代码有很多工厂的实现时,就会要求有相同的实现逻辑,这样才能保证开发者从一个工厂转到另一个工厂时不用担心它的用法(只需要调用工厂方法,并且方法名都一样)。
这样说可能很抽象,让我们回到之前车的例子,这可能不是一个好的例子,但是我用这个例子可以说明工厂方法模式和简单工厂模式的区别(后面会有实际的例子让你明白该模式的强大之处):
/the products
public interface Car {
public void drive();
}
public class Mustang implements Car{
public void drive() {
// some stuff
}
...
}
public class Ferrari implements Car{
public void drive() {
// some stuff
}
...
}
/ the factory
//the definition of the interface
public interface CarFactory{
public Car getCar() {}
}
//the real factory with an implementation of the getCar() factory method
public class ConcreteCarFactory implements CarFactory{
//this class is instantiable
public CarFactory(){
//some stuff
}
public Car getCar() {
// choose which car you want
return new Ferrari();
}
}
...
/some code using the factory
public static void someFunctionInTheCode(){
CarFactory carFactory = new ConcreteCarFactory();
Car myCar = carFactory.getCar();
myCar.drive();
}
比较这个例子和之前的例子,你会发现这里添加了一个接口(CarFactory
),实际的工厂(ConcreteCarFactory
)实现了这个接口。
我之所以说这不是一个好的例子是因为,这里只有一个具体的工厂类,所以并不需要用到工厂方法模式。只有当我有许多具体的工厂(SportCarFactory
、VintageCarFactory
、LuxeCarFactory
、CheapCarFactory
)需要实现时,该模式才会有用。这样的话,开发者可以很容易从一个工厂切换到另一个工厂,只需要使用相同工厂方法getCar()
即可。
实例
Java API
在 Java 中,一个常见的例子是集合类的iterator()
方法,每一个集合都实现了Iterable<E>
接口,该接口描述了iterator()
方法,该方法会返回一个Iterable<E>
实例。ArrayList<E>
是一个集合,所以它实现了Iterable<E>
接口以及工厂方法iterator()
,该方法会返回Iterator<E>
的子类的实例。
//here is a simplified definition of an iterator from the java source code
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
//here comes the factory interface!
public interface Iterable<T> {
Iterator<T> iterator();
}
//here is a simplified definition of ArrayList from the java source code
//you can see that this class is a concrete factory that implements
//a factory method iterator()
//Note : in the real Java source code, ArrayList is derived from
//AbstractList which is the one that implements the factory method pattern
public class ArrayList<E> {
//the iterator() returns a subtype and an "anonymous" Iterator<E>
public Iterator<E> iterator()
{
return new Iterator<E>()
{
//implementation of the methods hasNext(), next() and remove()
}
}
...
}
这里给出ArrayList
的标准使用方法
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Example {
public static void main(String[] ars){
//instantiation of the (concrete factory) ArrayList
List<Integer> myArrayList = new ArrayList<>();
//calling the factory method iterator() of ArrayList
Iterator<Integer> myIterator = myArrayList.iterator();
}
}
我展示的是ArrayList
的例子,HashSet
、LinkedList
或者HashMap
都是一样的,他们都是集合类。这一设计模式的优点在于,你无需知道你所使用的集合是什么,每一个集合都会通过工厂方法iterator()
提供一个Iterator
。
另一个好的例子就是 Java 8 集合中的stream()
方法。
Spring
Spring 框架是基于工厂方法模式的。ApplicationContext
实现了BeanFactory
接口,该接口描述了一个方法Object getBean(param)
,该方法返回一个Object
类的实例。这个例子很有趣,因为 Java 中的所有类都继承自Object
类,所以该工厂可以返回所有类的实例(具体取决于param
)。
public class Example{
public static void main(String[] args) {
//creation of the BeanFactory
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
//creation totaly different type of objets with the factory
MyType1 objectType1 = context.getBean("myType1");
MyType2 objectType2 = context.getBean("myType2");
}
}
抽象工厂
重量级的来了!该工厂在"Gang of Four"的书中是这样描述的:
“Provide an interface for creating families of related or dependent objects without specifying their concrete classes”
如果你没有看明白这句话,这很正常,不用担心,它被叫作抽象工厂不是没有原因的。
我把抽象工厂看作是工厂方法模式的泛化,只不过这里工厂接口有许多相关的工厂方法,希望这样解释可以帮到你。关于相关的这个说法,我是想说明它们是概念相关的,它们可以形成一个工厂方法集。让我们来看看它的 UML 图和工厂方法模式有什么不同:
Factory
是一个接口,其中定义了很多的工厂方法,我们给出的例子中有两个:getObject1()
和getObject2()
,每个方法创建了一个Type
类或其子类的实例。ConcreteFactory
类实现了Factory
接口,故而实现了它自己的getObject1()
和getObject2()
方法,现在假设一种情况:两个具体工厂,其中一个可以返回SubType1.1
和SubType2.1
,另一个可以返回SubType1.2
以及SubType2.2
。
因为这些确实很抽象,让我们回到之前CarFactory
的例子。
在工厂方法模式中,工厂接口有一个方法:getCar()
,而抽象工厂中,其接口可能有三个方法:getEngine()
、getBody()
和getWheel()
,你可以有很多具体的工厂:
SportCarFactory
类可以返回PowerfulEngine
类、ReceCatBody
类和RaceCarWheel
类的实例CheapCarFactory
类可以返回WeakEngine()
类、HorribleBody
类和RottenWheel
类的实例
如果你想制造一个 sport car,你需要实例化一个SportCarFactory
,然后再使用它;如果你想制造一个 cheap car,你需要实例化一个CheapCarFactory
。
抽象工厂的三个工厂方法都是相关的,他们都是 car 产品的概念。当然,工厂方法可以有参数,所以它们可以返回不同的类型,例如,getEngine(String model)
方法可以返回 Ferrari458Engine、或者 FerrariF50Engine、又或者 Ferrari450Engine,取决于传入的参数。
这里给出具体的代码(只有SportCarFactory
和两个工厂方法)
/the different products
public interface Wheel{
public void turn();
}
public class RaceCarWheel implements Wheel{
public void turn(){
// some stuff
}
...
}
public interface Engine{
public void work();
}
public class PowerfulEngine implements Engine{
public void work(){
// some stuff
}
...
}
/the factory
public interface CarFactory{
public Engine getEngine();
public Wheel getWheel();
}
public class SportCarFactory implements CarFactory{
public Engine getEngine(){
return new PowerfulEngine();
}
public Wheel getWheel(){
return new RaceCarWheel();
}
}
/some code using the factory
public class SomeClass {
public void someFunctionInTheCode(){
CarFactory carFactory = new SportCarFactory();
Wheel myWheel= carFactory.getWheel();
Engine myEngine = carFactory.getEngine();
}
}
抽象工厂并简单,所以你应该什么时候使用它呢?
永远不要使用它!!!,很难说。我将这种工厂模式看作是一种组织代码的方式。如果你最后发现你使用了很多个相关的工厂方法模式,你就可以使用一个抽象工厂模式来组织它们。我并不推崇那种“我们未来可能会需要一个抽象工厂模式,所以我们现在要使用抽象工厂模式的做法”,因为抽象工厂模式确实非常抽象。我更喜欢以简单的方式实现,然后在必要的时候重构。
然而,一个常见的用例是,你需要创建不同外观的用户见面,Gang of Four 使用这个例子去说明抽象工厂模式。用户界面需要一些类似于window、scroll bar、buttons这种的组件,你可以为每种外观创建一个具体工厂。显然这个例子是在互联网时代来临之前的,现如今你可以使用 CSS(或者一些脚本语言) 来改变组件的外观,即使是桌面应用,也就是说,大多数时候静态工厂方法就足够了。
但是如果你确实想使用这种模式,GoF 给出了一些案例:
“a system should be configured with one of multiple families of products”
“you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations”
实例
大多数 DAO(Data Access Object) 框架都使用了抽象工厂来确定每一个具体工厂的基本操作,当然不同框架的方法名会有差异,但是大致为:
createObject()
或者persistObject()
updateObject()
或者saveObject()
deleteObject()
或者removeObject()
readObject()
或者findObject()
对于你操作的不同的对象,你都需要一个具体的工厂,例如,你需要通过数据库管理 people、houses 和 contracts,那么你就需要PersonFactory
、HouseFactory
以及ContractFactory
。
Spring 中的CrudRepository
是一个很好的抽象工厂的实现。
如果你想要具体的 Java 代码,你可以看看 JPA、Hibernate 或者 SpringData 的介绍。
总结
希望现在你在使用不同类型的工厂模式的时候,能很好的驾驭它们。不过正如我在文章中多次提到的那样,大多数时候,使用工厂模式会使得代码更复杂,更抽象。即便你知道这些工厂(如果你不知道,请重新读一下这篇文章!),也可能你的同事不知道呢?不过,在做中大型应用时,工厂模式值得一用。
在理解不同的工厂的过程中,我挣扎了很长一段时间,如果有一篇像本文一样的文章,它一定可以帮到我。我希望本文是易于理解的,然后文章没有犯太多的错,如果你在阅读本文的过程中有什么困扰,请告知我,以便我改进这篇文章
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)