0x01 前言
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
0x02 简介
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
应用实例:1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。2、 Hibernate 换数据库只需换方言和驱动就可以。
优点:1、一个调用者想创建一个对象,只要知道其名称就可以了。2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:1、每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景:1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
0x03 设计概述
简要概述
“工业革命”之前:客户需要一台联想电脑,客户直接创建一台联想电脑,直接拿去用。
简单工厂(非设计模式)时代:后来出现“工业革命”。客户不用去创建电脑,因为客户有一个工厂来帮他创建电脑。想要什么系列电脑,这个工厂就可以创建。比如想要联想系列电脑。工厂就创建这个系列的电脑。即工厂可以创建产品。
工厂方法模式时代:为了满足客户的需求,电脑的系列越来越多,如联想、华硕、宏碁等系列,一个工厂无法创建所有的电脑。于是由单独分出来多个具体的工厂,每个具体工厂创建一种系列。而这么多的具体的工厂需要一个统一的电脑工厂来规范,即创办统称,即电脑工厂。当生产某一个具体系列的电脑,由于电脑工厂是抽象的,所以你需要指定某个具体的工厂才能生产车出来。
抽象工厂模式时代:随着客户的要求越来越高,需要定制不同组合的电脑。例如联想 Y 系列,华硕 G 系列,联想 Y 配备性能级显卡,联想 G 配备发烧级显卡。那么并不需要创办一个生产联想 Y 性能级显卡电脑的工厂、华硕 G 发烧级显卡电脑的工厂。而仅仅是抽象一个工厂,用来生产联想 Y 电脑、华硕 G 电脑、性能级显卡、发烧级显卡,然后组合就形成产品。
0x04 具体实现
“工业革命”之前
“工业革命”之前,生产电脑的方式如此简单,客户需求什么电脑就创建什么电脑。即创建对象只需要创建对应的类就可以获得对象。
这里引入一个问题,谈谈为什么要使用工厂类的设计模式。我们经常一些功能类似的类,所以我们的思路是对进行抽象,使用接口暴露公共的方法,并通过实现类来提供具体的实现。
产品类
// factory_pattern.general.product.IComputer
package factory_pattern.general.product;
public interface IComputer {
void getComputer(String brand);
}
// factory_pattern.general.product.impl.LenovoComputer
package factory_pattern.general.product.impl;
import factory_pattern.general.product.IComputer;
public class LenovoComputer implements IComputer {
@Override
public void getComputer(String brand) {
System.out.println("生产一台 " + brand + " 笔记本电脑");
}
}
// factory_pattern.general.product.impl.AsusComputer
package factory_pattern.general.product.impl;
import factory_pattern.general.product.IComputer;
public class AsusComputer implements IComputer {
@Override
public void getComputer(String brand) {
System.out.println("研发一台 " + brand + " 台式机电脑");
}
}
用户类
// factory_pattern.general.user.ComputerTest
package factory_pattern.general.user;
import org.junit.Test;
import factory_pattern.general.product.IComputer;
import factory_pattern.general.product.impl.AsusComputer;
import factory_pattern.general.product.impl.LenovoComputer;
public class ComputerTest {
@Test
public void testGetComputer() {
IComputer lenPC = new LenovoComputer();
lenPC.getComputer("Lenovo");
IComputer asusPC = new AsusComputer();
asusPC.getComputer("Asus");
}
}
简单工厂
在“工业革命”之前(没使用简单工厂时),这些类的具体实现方法形成了一个问题,每个类的具体实现方法参数不一样,具体实现方法(这里指打印的内容)也不一样,每次调用实现方法都很麻烦。
还有一个问题,客户类和产品类紧密耦合在一起,为了解耦,我们需要引入一个第三方,用于创建对象,即生产电脑。
所以我们需要进行工业革命,即封装成简单工厂模式。建立一个工厂,用这个工厂来替客户创建电脑。
产品类代码我们不需要修改,加入用于创建对象的工厂类和修改客户类代码。
工厂类
// factory_pattern.simple.factory.SimpleFactory
package factory_pattern.simple.factory;
import factory_pattern.simple.product.IComputer;
import factory_pattern.simple.product.impl.LenovoComputer;
import factory_pattern.simple.product.impl.AsusComputer;
public class SimpleFactory {
private SimpleFactory() {
}
public static IComputer createComputer(String brand) {
IComputer computer = null;
switch (brand) {
case "Lenovo":
computer = new LenovoComputer();
break;
case "Asus":
computer = new AsusComputer();
break;
default:
throw new IllegalArgumentException();
}
return computer;
}
}
用户类
// factory_pattern.simple.user.ComputerTest
package factory_pattern.simple.user;
import org.junit.Test;
import factory_pattern.simple.product.IComputer;
import factory_pattern.simple.factory.SimpleFactory;
public class ComputerTest {
@Test
public void testGetComputer() {
IComputer lenPC = SimpleFactory.createComputer("Lenovo");
lenPC.getComputer("Lenovo");
IComputer asusPC = SimpleFactory.createComputer("Asus");
asusPC.getComputer("Asus");
}
}
简单工厂又称静态工厂方法,它存在的目的很简单:定义一个用于创建对象的接口。
静态工厂的组成:
工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,用来创建产品。
抽象产品角色:它一般是具体产品继承的父类或者实现的接口。
具体产品角色:工厂类所创建的对象就是此角色的实例,在 Java 中由一个具体类实现。
上述的简单工厂不利于拓展,违背了“开闭原则”。每增加一个类,就要修改工厂类。什么是“开闭原则”?开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的。所以将引出下面的工厂方法模式。
工厂方法模式
工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
工厂方法模式组成:
抽象工厂角色:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
工厂方法模式使得结构变得灵活起来。当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代码。可以看出工厂角色的结构也是符合开闭原则的!具体实现如下:
项目结构图
工厂类
// factory_pattern.factory_method.factory.IFactory
package factory_pattern.factory_method.factory;
import factory_pattern.factory_method.product.IComputer;
public interface IFactory {
IComputer createComputer();
}
// factory_pattern.factory_method.factory.impl.LenovoFactory
package factory_pattern.factory_method.factory.impl;
import factory_pattern.factory_method.factory.IFactory;
import factory_pattern.factory_method.product.IComputer;
import factory_pattern.factory_method.product.impl.LenovoComputer;
public class LenovoFactory implements IFactory {
@Override
public IComputer createComputer() {
return new LenovoComputer();
}
}
// factory_pattern.factory_method.factory.impl.AsusFactory
package factory_pattern.factory_method.factory.impl;
import factory_pattern.factory_method.factory.IFactory;
import factory_pattern.factory_method.product.impl.AsusComputer;
import factory_pattern.factory_method.product.IComputer;
public class AsusFactory implements IFactory {
@Override
public IComputer createComputer() {
return new AsusComputer();
}
}
用户类
// factory_pattern.factory_method.user.ComputerTest
package factory_pattern.factory_method.user;
import org.junit.Test;
import factory_pattern.factory_method.factory.impl.AsusFactory;
import factory_pattern.factory_method.factory.impl.LenovoFactory;
import factory_pattern.factory_method.product.IComputer;
public class ComputerTest {
@Test
public void testGetComputer() {
IComputer lenPC = new LenovoFactory().createComputer();
lenPC.getComputer("Lenovo");
IComputer asusPC = new AsusFactory().createComputer();
asusPC.getComputer("Asus");
}
}
上述就是工厂方法的具体实现,可是还有一个问题,代码变得多了,因为功能类似的产品我们进行 2 层抽象,针对每个产品我们还抽象出了 2 层的工厂类。在某个具体的业务场景中,不单单是只实例化一个类。但当我们在解决某些问题的时候,例如:举一个例子,我们生产各种品牌的笔记本,笔记本对应的各种性能的显卡。针对现在的情况我们是不是可以让一个工厂既生产对应品牌的笔记本,又生产对应性能的显卡呢?这就是抽象工厂模式。简单来说,可以把有一些有联系或者相近的产品,放到一个工厂去生产,没有必要单独再开一个工厂了。
抽象工厂模式作为一种设计模式,将在另一篇文章中单独解释。