工厂方法模式和抽象工厂模式都属于创建型的设计模式。我们主要在对象的创建逻辑比较复杂的情况下使用这两种模式来隐藏创建的细节,这两种模式针对的产品的复杂度不同,但又有很大的相似,因此本篇将两者放到一起来加深对二者的理解。
在介绍这两种设计模式之前,我们先了解一下简单工厂模式,这种模式不在 GoF 23 种设计模式之列。
1. 简单工厂模式
简单工厂模式适用于产品等级比较简单的系列对象的创建。
这里需要先了解两个重要的概念,产品等级结构和产品族。
产品等级结构可以看作是同一类型的产品的集合,比如手机,有华为品牌的、有 OPPO 品牌的、有 VIVO 品牌的等等。这些品牌的手机共同构成了手机这类产品的产品等级结构。
产品族可以看作是同一个制造商下所有的产品类型的集合,比如华为公司不仅生产手机,还生产平板、笔记本电脑、路由器等等。华为公司生产的这些产品类型,构成了一个产品族。
简单工厂模式比较适合产品等级结构比较固定且简单的类型的产品的创建。
比如我们定义一个路由器接口,且市面上有两种路由器厂商:华为和小米。
产品接口定义及实现
public interface IRouter {
void route();
}
public class HuaweiRouter implements IRouter {
@Override
public void route() {
System.out.println("华为路由器连接你我他!");
}
}
public class MiRouter implements IRouter {
@Override
public void route() {
System.out.println("小米路由器就是不一样!");
}
}
工厂类
public class RouterFactory {
public static IRouter createRouter(Class<? extends IRouter> routerClass) {
try {
return routerClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
throw new IllegalArgumentException("routerClass " + routerClass.getName() + " instantiate failed!");
}
}
测试
public class SimpleFactoryTest {
public static void main(String[] args) {
IRouter router = RouterFactory.createRouter(HuaweiRouter.class);
router.route();
router = RouterFactory.createRouter(MiRouter.class);
router.route();
}
}
类关系图
优缺点
简单工厂可以使用户不必关心复杂类的创建过程,实现了控制反转,但是当产品等级结构发生变化时,我们不得不修改工厂类的实现逻辑,这样违反了开闭原则。
2. 工厂方法模式
工厂方法又被称为工厂的工厂。针对本例来说,当华为和小米这两个品牌的路由器产品等级结构变得丰富时,即每个品牌下面的路由器分别有各式各样的型号,这时候我们需要为每个品牌提供一个工厂,这样可以将工厂的职能细化,避免因其中一个品牌的产品等级结构发生变化而调整整个大工厂的逻辑。这样也刚好遵循了接口隔离原则。
接口定义
// 路由器顶层抽象
public interface IRouter {
void route();
}
// 华为品牌路由器的抽象
public interface IHuaweiRouter extends IRouter {
// 定义扩展
}
// 小米品牌路由器的抽象
public interface IMiRouter extends IRouter {
// 定义扩展
}
// 工厂的抽象
public interface IRouterFactory {
IRouter createRouter(Class<? extends IRouter> routerClass);
}
产品接口实现
// 华为 AX3 路由器,实现华为品牌路由器的规范
// 华为品牌的路由器的产品等级结构发生变化,只要增加实现即可
public class HuaweiAX3Router implements IHuaweiRouter {
public void route() {
System.out.println("华为路由,覆盖无忧!");
}
}
// 小米 4C 路由器,实现小米品牌路由器的规范
// 华为品牌的路由器的产品等级结构发生变化,只要增加实现即可
public class Mi4CRouter implements IMiRouter {
@Override
public void route() {
System.out.println("小米路由器就要快人一步!");
}
}
工厂实现
// 华为路由器的工厂,设计成单例
public class HuaweiRouterFactory implements IRouterFactory {
private HuaweiRouterFactory(){
// 防止反射机制破坏单例
if (Holder.INSTANCE != null) {
throw new IllegalArgumentException("instance has been created!");
}
}
public static HuaweiRouterFactory getInstance() {
return Holder.INSTANCE;
}
@Override
public IHuaweiRouter createRouter(Class<? extends IRouter> routerClass) {
if (routerClass.isAssignableFrom(IHuaweiRouter.class)) {
throw new IllegalArgumentException("routerClass "
+ routerClass.getName() + " is not a subclass of IHuaweiRouter!");
}
try {
return (IHuaweiRouter) routerClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
throw new IllegalArgumentException("routerClass "
+ routerClass.getName() + " instantiate failed!");
}
private static class Holder {
private static final HuaweiRouterFactory INSTANCE = new HuaweiRouterFactory();
}
}
// 小米路由器的工厂,设计成单例
public class MiRouterFactory implements IRouterFactory {
// 内存屏障保证每次读取到的是最新的值
private static volatile MiRouterFactory INSTANCE;
private MiRouterFactory() {
if (INSTANCE != null) {
throw new IllegalArgumentException("instance has been created!");
}
}
public static MiRouterFactory getInstance() {
// DCL 确保线程安全同时兼顾效率
if (INSTANCE == null) {
synchronized (MiRouterFactory.class) {
if (INSTANCE == null) {
INSTANCE = new MiRouterFactory();
}
}
}
return INSTANCE;
}
public IMiRouter createRouter(Class<? extends IRouter> routerClass) {
if (routerClass.isAssignableFrom(IMiRouter.class)) {
throw new IllegalArgumentException("routerClass "
+ routerClass.getName() + " is not a subclass of IMiRouter!");
}
try {
return (IMiRouter) routerClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
throw new IllegalArgumentException("routerClass "
+ routerClass.getName() + " instantiate failed!");
}
}
public class FactoryMethodTest {
public static void main(String[] args) {
IHuaweiRouter huaweiRouter = HuaweiRouterFactory.getInstance().createRouter(HuaweiAX3Router.class);
huaweiRouter.route();
IMiRouter miRouter = MiRouterFactory.getInstance().createRouter(Mi4CRouter.class);
miRouter.route();
}
}
测试
public class FactoryMethodTest {
public static void main(String[] args) {
IHuaweiRouter huaweiRouter = HuaweiRouterFactory.getInstance().createRouter(HuaweiAX3Router.class);
huaweiRouter.route();
IMiRouter miRouter = MiRouterFactory.getInstance().createRouter(Mi4CRouter.class);
miRouter.route();
}
}
类图
优缺点
工厂方法的设计符合开闭原则,当产品等级结构发生变化时,只需要扩展顶层接口或者增加实现即可。但是这种模式也使得我们的代码变得臃肿,对于本例而言至少一个品牌要对应一个工厂,增加了我们的维护成本。
3. 抽象工厂模式
抽象工厂是对复杂产品(产品族)的创建的抽象。我们在上例的基础上进行扩展,假设华为和小米现在增加了产品线,不仅生产路由器,同时也生产网络电缆和交换机,这时我们如果要对华为工厂和小米工厂进行抽象,就不仅仅只定义路由器的规范,还要定义网络电缆和交换机的规范,此时这两个品牌也不再是简单的路由器厂商,而成了通信设备供应商,他们生产的相关产品也必须符合国标规范,这个国标规范就是对应产品的抽象。
接口定义
// 路由器的抽象
public interface IRouter {
void route();
}
// 电缆的抽象
public interface ICable {
void link();
}
// 交换机的抽象
public interface IExchanger {
void exchange();
}
// 通信设备厂商的抽象
public abstract class CommunicationDeviceFactory {
public void init() {
System.out.println("正在进行工厂初始化...");
}
public abstract IRouter createRouter();
public abstract ICable createCable();
public abstract IExchanger createExchanger();
}
接口实现
// 华为电缆
public class HuaweiCable implements ICable {
@Override
public void link() {
System.out.println("华为电缆连接你我他!");
}
}
// 华为交换机
public class HuaweiExchanger implements IExchanger {
@Override
public void exchange() {
System.out.println("华为交换机连接你我他!");
}
}
// 华为路由器
public class HuaweiRouter implements IRouter {
@Override
public void route() {
System.out.println("华为路由器连接你我他!");
}
}
// 华为工厂,实际中一般设计成单例
public class HuaweiFactory extends CommunicationDeviceFactory {
public HuaweiFactory() {
super.init();
}
@Override
public IRouter createRouter() {
return new HuaweiRouter();
}
@Override
public ICable createCable() {
return new HuaweiCable();
}
@Override
public IExchanger createExchanger() {
return new HuaweiExchanger();
}
}
// 小米电缆
public class MiCable implements ICable {
@Override
public void link() {
System.out.println("小米电缆就是不一样!");
}
}
// 小米交换机
public class MiExchanger implements IExchanger {
@Override
public void exchange() {
System.out.println("小米交换机就是不一样!");
}
}
// 小米路由器
public class MiRouter implements IRouter {
@Override
public void route() {
System.out.println("小米路由器就是不一样!");
}
}
// 小米工厂,实际中一般设计成单例
public class MiFactory extends CommunicationDeviceFactory {
public MiFactory () {
super.init();
}
@Override
public IRouter createRouter() {
return new MiRouter();
}
@Override
public ICable createCable() {
return new MiCable();
}
@Override
public IExchanger createExchanger() {
return new MiExchanger();
}
}
测试
public class AbstractFactoryTest {
public static void main(String[] args) {
ICommunicationDeviceFactory factory = new HuaweiFactory();
factory.createCable().link();
factory.createExchanger().exchange();
factory.createRouter().route();
factory = new MiFactory();
factory.createCable().link();
factory.createExchanger().exchange();
factory.createRouter().route();
}
}
类图
优缺点
抽象工厂模式将一系列相关的产品(产品族)的创建工作交给一个统一的工厂完成,使应用层不需要关心创建的细节。但是它最大的弊端就是当产品族发生变化是,需要修改顶层的接口及相应实现。因此抽象工厂比较适合与产品族比较稳定的一系列复杂产品的创建。
4. 对比
名称 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
工厂方法 | 适用于产品结构等级不常发生变化的产品的创建 | 将同一产品等级结构的产品的创建进行抽象,符合单一职责及开闭原则 | 类的数量增加,增加维护成本 |
抽象工厂 | 适合产品结构复杂的产品的创建,尤其是一系列相关产品(产品族)的创建 | 将同一产品族的产品统一到一个工厂中,应用层不需要关心创建的细节 | 当产品族发生变化需要修改顶层接口及工厂实现,不易于扩展 |