Java工厂模式详解

Java工厂模式详解

@author:Jingdai
@date:2021.05.24

工厂模式只包括两个,一个是工厂方法(Factory Method)模式,一个是抽象工厂(Abstract Factory)模式,但是我们平时也经常听到简单工厂模式和静态工厂模式,它们有什么关系呢?希望这篇文章能解决你的问题。

简单工厂、静态工厂

首先说明,简单工厂并不属于23种设计模式中的某一种,它更像是一种编程习语。

本文中的例子部分来自于深入浅出设计模式,看下面这个例子。

Pizza orderPizza(String type) {
    Pizza pizza;

    if (type.equals("cheese")) {
        pizza = new CheesePizza();
    } else if (type.equals("greek")) {
        pizza = new GreekPizza();
    } else if (type.equals("pepperoni")) {
        pizza = new PepperoniPizza();
    }

    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}

为了更好的灵活性,使orderPizza() 方法的返回值 Pizza 是一个抽象类,这样就可以返回任何 Pizza 类的子类。但是,由上面代码可见,虽然 Pizza 类是一个抽象类,但是我们实例化的时候还是必须实例化一个Pizza的子类,因为 Java 中抽象类和接口是不能实例化的,所以这里只能通过 new Pizza的子类进行创建对象并返回,也就是说这里的orderPizza() 方法仍然依赖于具体的子类,而不是依赖于抽象类。如果今后又有了新的Pizza品种,比如ClamPizza,那么就需要改变 orderPizza() 方法,这就违反了开闭原则(对拓展开放,对修改关闭)。当然如果如果你明确知道一个类以后不会拓展,那就不会有任何问题,也就不需要使用工厂模式。那简单工厂是怎么解决这个问题的呢?其实简单工厂就是将Pizza对象的创建封装了起来,看下面利用简单工厂实现的代码。

PizzaStore

public class PizzaStore {

    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = factory.createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    // other methods here
}

SimplePizzaFactory

public class SimplePizzaFactory {

    public Pizza createPizza(String type) {
        Pizza pizza = null;

        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("greek")) {
            pizza = new GreekPizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        }
        return pizza;
    }

}

这样就完成了一个简单工厂的设计,这样看的话仅仅是把变化的部分从一个地方移动到了另一个地方,并没有解决什么问题,但是要注意,我们这里仅仅只有一个 orderPizza() 方法使用了工厂,但是实际上很可能还有很多地方都使用到这个工厂,以后需要创建Pizza的地方仅仅需要依赖于简单工厂,把创建Pizza的任务交给简单工厂,这样一来以前需要在各个地方修改代码,而以后只需要在简单工厂的代码中修改就行了。

那静态工厂又是什么呢?看上面的代码,每次我们需要pizza都需要创建一个SimplePizzaFactory对象,可以直接把这个创建pizza的方法改为静态的,那样的话之后再需要pizza就不需要创建工厂对象了,直接调用静态方法就行了,这样的简单工厂就称为静态工厂。

再次强调一下,简单工厂和静态工厂并不属于23种设计模式,它还是没有解决违反开闭原则的问题。

工厂方法模式

还是上面的需求,这次我们用工厂方法模式去解决Pizza需求经常变化的问题。我们可以对每一种Pizza都创建一个具体的工厂,用具体的工厂去创建对应的Pizza。

既然有多种工厂,那就需要一个工厂基类,多个工厂类去继承这个工厂基类。同时,由于Pizza的类型已经由工厂类型决定了,所以createPizza() 方法不再需要 type 参数。

改变后的代码如下。

PizzaStore

public class PizzaStore {

    PizzaFactory factory;

    public PizzaStore(PizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza() {
        Pizza pizza;

        pizza  = factory.createPizza();

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

}

PizzaFactory

public abstract class PizzaFactory {

    public abstract Pizza createPizza();
}

CheesePizzaFactory

public class CheesePizzaFactory extends PizzaFactory{

    @Override
    public Pizza createPizza() {
        return new CheesePizza();
    }
}

上面只举了一个CheesePizzaFactory的例子,其它的Pizza对应的工厂也类似。这样写完之后就实现了工厂方法模式。再回头看 PizzaStore 这个类,它全部依赖于抽象,Pizza是抽象的,PizzaFactory也是抽象的,它对之后具体是什么工厂创建什么Pizza一无所知,这些工作完全交给了具体的工厂子类。当传递一个CheesePizzaFactory时,它就是CheesePizza;当传递一个GreekPizzaFactory时,它就是GreekPizza,其他同理。也就是说他把对象的实例化交给了子类,即延迟到子类实现,完全由子类决定具体创建的对象。

再看看工厂方法是否满足我们的要求。当我们增加了一个Pizza需求,比如ClamPizza,这时我们只需要加一个ClamPizza类和一个ClamPizzaFactory类就行,PizzaStore类完全不需要任何改动,满足开闭原则。当然,使用PizzaStore类的那个类肯定是需要修改的,需要将参数改为ClamPizzaFactory的对象,这就不是我们关心的内容了,由使用它的类去完成相应的需求。

下面看工厂方法的官方定义。

工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

对照上面的例子估计就不难理解了。

抽象工厂模式

首先看下面这个需求,我们的一个订单Dao需要与数据库进行交互,要创建3个对象,Connection、Statement和ResultSet,利用上面讲的工厂方法模式,可以写出下面的代码,下面仅写了Connection相关的代码,Statement和ResultSet类似。

OrderDao

public class OrderDao {

    ConnectionFactory connectionFactory;
    StatementFactory statementFactory;
    ResultSetFactory resultSetFactory;

    public OrderDao(ConnectionFactory connectionFactory,
        StatementFactory statementFactory, 
        ResultSetFactory resultSetFactory) {
        
        this.connectionFactory = connectionFactory;
        this.statementFactory = statementFactory;
        this.resultSetFactory = resultSetFactory;
    }

    public Order getOrderById(Integer id) {
        Connection connection = connectionFactory.createConnection();
        Statement statement = statementFactory.createStatement();
        ResultSet resultSet = resultSetFactory.createResultSet();

        // some xxx operations
        
        return order;
    }
}

Connection

public abstract class Connection {
    // xxx
}

OracleConnection

public class OracleConnection extends Connection{
    // xxx
}

ConnectionFactory

public abstract class ConnectionFactory {

    public abstract Connection createConnection();

}

OracleConnectionFactory

public class OracleConnectionFactory extends ConnectionFactory{

    @Override
    public Connection createConnection() {
        return new OracleConnection();
    }

}

仔细观察上面的代码,好像也没什么问题,但是其实是有一些问题的,OrderDao使用的时候,会使用三个具体的工厂实例,比如用Oracle的数据库,那就是OracleConnectionFactory、OracleStatementFactory和OracleResultSetFactory,这三个工厂创建的三个实例OracleConnection、OracleStatement和OracleResultSet是相互关联的,即同一数据库的三个类应该是一致的,比如OracleConnection肯定是不能与MySQLStatement相互操作。那怎么解决这个问题呢?由于这三个类是相互关联的,是一个家族的,那么我们就应该用一个工厂来创建这三个对象,修改后的代码如下。

OrderDao

public class OrderDao {

    DBFactory dbFactory;

    public OrderDao(DBFactory dbFactory) {
        this.dbFactory = dbFactory;
    }

    public Order getOrderById(Integer id) {
        Connection connection = dbFactory.createConnection();
        Statement statement = dbFactory.createStatement();
        ResultSet resultSet = dbFactory.createResultSet();

        // some xxx operations
        
        return order;
    }
}

DBFactory

public abstract class DBFactory {

    public abstract Connection createConnection();
    public abstract Statement createStatement();
    public abstract ResultSet createResultSet();

}

OracleDBFactory

public class OracleDBFactory extends DBFactory{

    @Override
    public Connection createConnection() {
        return new OracleConnection();
    }

    @Override
    public Statement createStatement() {
        return new OracleStatement();
    }

    @Override
    public ResultSet createResultSet() {
        return new OracleResultSet();
    }

}

Connection

public abstract class Connection {
    // xxx
}

OracleConnection

public class OracleConnection extends Connection{
    // xxx
}

这样就完成了抽象工厂模式,由于这里只有一个工厂,当我们用一个OracleDBFactory实例化这个变量后,它创建的三个对象肯定是Oracle相关的三个对象,这样就解决了上面的三个相关联的对象的创建问题。

可以看出,抽象工厂模式和工厂方法模式非常相似,抽象工厂里面的每个方法的实现其实就是一个工厂方法模式,它的优点就是可以创建一系列相互依赖的对象,如果抽象工厂里只有一个创建对象的方法,那抽象工厂模式就变成了一个工厂方法模式。

下面看抽象工厂的官方定义。

抽象工厂提供了一个接口,让该接口负责创建一系列相关/相互依赖的对象,而无需指定它们具体的类。

总结

  • 不管是简单工厂、工厂方法还是抽象工厂,它们都是通过某种方法绕开 new 操作,避免 new 操作带来的紧耦合,将对象的实例化移到本方法之外。
  • 如果没有多个相关联的对象需要创建,就没有必要使用抽象工厂模式,仅仅使用工厂方法模式就可以。

参考

  • 深入浅出设计模式
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值