简单工厂模式和工厂方法模式
文章首发于我的个人博客,欢迎访问:https://blog.itzhouq.cn/factory
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 Java里边共有23种设计模式而工厂模式就有三种,它们分别是简单工厂模式(并不在23中模式之中),工厂方法模式以及抽象工厂模式,其中我们通常所说的工厂模式指的是工厂方法模式,工厂方法模式是日常开发中使用频率最高的一种设计模式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
1、介绍
意图: 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决: 主要解决接口选择的问题。
何时使用: 我们明确地计划不同条件下创建不同实例时。
如何解决: 让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码: 创建过程在其子类执行。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点: 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项: 作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
2、简单工厂模式
简单工厂模式实现
使用简单的例子说明什么是简单工厂模式。
假设现在有不同种类的车,消费者可以自行选择消费某种车。如果按照以前的思路是将车作为一个接口,不同的车实现这个接口,消费时,创建一个实现类,将车接口指向实现类。代码如下:
创建一个车的接口:
public interface Car {
void getName();
}
创建一个实现类,比如五菱宏光
public class Wuling implements Car {
@Override
public void getName() {
System.out.println("五菱宏光");
}
}
再创建一个实现类,比如特斯拉
public class Tesla implements Car {
@Override
public void getName() {
System.out.println("特斯拉");
}
}
现在模拟消费车的过程,比如我们需要买一辆五菱宏光,再买一辆特斯拉,来一个测试类:
public class FactoryTest {
public static void main(String[] args) {
Car car = new Wuling();
Car car2 = new Tesla();
car.getName(); // 五菱宏光
car2.getName(); // 特斯拉
}
}
可以看到我们每次消费车的时候都需要使用new
关键词来创建车对象。这其实是有问题。对于消费者来说,我不关心车是怎么造出来的,我只需要知道车的名称就好了,但是这里消费者需要知道车的结构,创建车对象需要哪些参数。这里的车对象创建的时候使用了无参构造器,实际的情况可能复杂一些。
为了解决这个问题我们可以引入一个工厂类,这个工厂类目的是制造各种车。消费者消费的时候直接根据车的名称从工厂中拿即可,不需要知道车的结构,不需要自己new
对象。
public class CarFactory {
public static Car getCar(String car) {
if ("五菱宏光".equalsIgnoreCase(car)) {
return new Wuling();
} else if ("特斯拉".equalsIgnoreCase(car)) {
return new Tesla();
} else {
return null;
}
}
}
现在再次模拟消费的过程:
public class FactoryTest {
public static void main(String[] args) {
// Car car = new Wuling();
// Car car2 = new Tesla();
// 使用工厂创建
Car car = CarFactory.getCar("五菱宏光");
Car car2 = CarFactory.getCar("特斯拉");
car.getName(); // 五菱宏光
car2.getName(); // 特斯拉
}
}
这样简单工厂模式就实现了。
简单工厂模式的核心本质:
- 实例化对象不需要
new
,用工程方法代替 - 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
简单工厂模式的问题
现在有一个需求:消费者需要购买大众牌的车。对于这个需求,我们需要修改新引入一个类Dazhong
,实现Car
接口,然后修改工厂类。
public class Dazhong implements Car {
@Override
public void getName() {
System.out.println("大众");
}
}
修改工厂:
public class CarFactory {
public static Car getCar(String car) {
if ("五菱宏光".equalsIgnoreCase(car)) {
return new Wuling();
} else if ("特斯拉".equalsIgnoreCase(car)) {
return new Tesla();
} else if ("大众".equalsIgnoreCase(car)) {
return new Dazhong();
} else {
return null;
}
}
}
添加了一个新的判断。消费的方式没有变化。
从设计的角度讲,简单工厂模式是不符合开闭原则。增加了一个新的产品,需要修改原来的代码。所以简单工厂模式也叫静态工厂模式。
下面使用示意图分析一下简单工厂模式。
为了解决简单工程模式的问题,我们引入工厂方法模式。
3、工厂方法模式
工厂方法模式是简单工厂的进一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。
车接口不变:
public interface Car {
void getName();
}
引入一个车的工厂接口,所有具体的车工厂都要实现这个接口:
public interface CarFactory {
Car getCar();
}
五菱宏光:五菱宏光车工厂和实现类:
public class WulingFactory implements CarFactory {
@Override
public Car getCar() {
return new Wuling();
}
}
public class Wuling implements Car {
@Override
public void getName() {
System.out.println("五菱宏光");
}
}
特斯拉:特斯拉工厂和实现类:
public class TeslaFactory implements CarFactory {
@Override
public Car getCar() {
return new Tesla();
}
}
public class Tesla implements Car {
@Override
public void getName() {
System.out.println("特斯拉");
}
}
模拟消费:
public class Consumer {
public static void main(String[] args) {
Car car = new WulingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
car.getName(); // 五菱宏光
car2.getName(); // 特斯拉
}
}
这就是工程方法模式,符合开闭原则。
对于需求,不需要修改现有的工厂类,而是增加一个对应的车工厂和实现类。比如,现在增加了一个摩拜车。
那我先写一个摩拜车类:
public class Mobai implements Car {
@Override
public void getName() {
System.out.println("摩拜");
}
}
再写一个摩拜车工厂:
public class MobaiFactory implements CarFactory {
@Override
public Car getCar() {
return new Mobai();
}
}
消费者消费这个车的车的时候,不需要修改CarFactory
,直接使用摩拜车工厂创建摩拜车。
public class Consumer {
public static void main(String[] args) {
Car car = new WulingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
Car car3 = new MobaiFactory().getCar();
car.getName(); // 五菱宏光
car2.getName(); // 特斯拉
car3.getName(); // 摩拜
}
}
和简单工厂对比一下,最根本的区别在于,简单工厂只有一个统一的工厂类,而工厂方法是针对每个要创建的对象都会提供一个工厂类,这些工厂类都实现了一个工厂基类(本例中的CarFactory
)。
示意图:
4、比较简单工厂模式和工厂方法模式
从设计原则比如开闭原则来看,工厂方法模式更好。但是从结构复杂度、代码复杂度、编程复杂度、管理复杂度上来说,简单工厂模式更方便,实际使用得更多。
5、工厂模式的应用场景
- JDK 中 Calendar 的 getInstance 方法
- JDBC 中 Connection 对象的获取
- Spring 中的 IOC 容器创建管理 bean对象
- 反射中 Class对象的 newInstance 方法。