抽象工厂模式(Abstract Factory Pattern)

792 阅读6分钟

在设计模式中按照不同的处理方式共包含三大类:创建型模式、结构型模式和行为模式。

创建型模式

  1. 工厂方法模式(Factory Method Pattern)
  2. 抽象工厂模式(Abstract Factory Pattern)
  3. 建造者模式(Builder Pattern)
  4. 原型模式(Prototype Pattern)
  5. 单例模式(Singleton Pattern)

工厂方法模式引入工厂等级结构,解决了简单工厂模式中工厂类职责过重的问题,但由于工厂方法模式中每个工厂只创建一类具体类的对象,这将会导致系统当中的工厂类过多,这势必会增加系统的开销。此时,我们可以考虑将一些相关的具体类组成一个“具体类族”,由同一个工厂来统一生产,这就是我们本文要说的“抽象工厂模式”的基本思想。

该模式中包含的角色和职责

抽象工厂(AbstractFactory)角色

抽象工厂,用于声明抽象定义具抽象产品的方法。比如获取电脑的Cpu,Mouse,Keyboard等抽象产品。

具体工厂(ConcreteFactory)角色

定义抽象工厂定义的方法。生成具体的产品。比如DellCpu,DellMouse,DellKeyboard这类具体产品

抽象产品(AbstractProduct)角色

抽象产品,定义一类产品对象的接口。

具体产品(ConcreteProduct)角色

具体的产品实现,比如DellCpu,DellMouse,DellKeyboard这些具体的产品信息定义类。

实例的的UML图

image.png

下面是具体的代码

抽象工厂(AbstractFactory)产品角色

Computer

/**
 * @author yangqiang
 * @date 2021-07-16 17:42
 */
public interface Computer {
    Cpu getCpu();
    Keyboard getKeyboard();
    Mouse getMouse();
}

具体工厂(ConcreteFactory)角色

DellFactory

/**
 * @author yangqiang
 * @date 2021-07-16 19:22
 */
public class DellFactory  implements Computer {
​
    @Override
    public Cpu getCpu() {
        return new DellCpu();
    }
​
    @Override
    public Keyboard getKeyboard() {
        return new DellKeyboard();
    }
​
    @Override
    public Mouse getMouse() {
        return new DellMouse();
    }
}

抽象产品(AbstractProduct)角色

Cpu

/**
 * @author: yangqiang
 * @create: 2021-07-16 19:30
 */
public interface Cpu {
    void operation();
}

Mouse

/**
 * @author: yangqiang
 * @create: 2021-07-16 19:29
 */
public interface Keyboard {
    void operation();
}

Keyboard

/**
 * @author yangqiang
 * @date 2021-07-16 19:22
 */
public interface Mouse {
    void operation();
}

具体产品(ConcreteProduct)角色

DellCpu

/**
 * @author yangqiang
 * @date 2021-07-16 19:34
 */
public class DellCpu implements Cpu {
    @Override
    public void operation() {
        System.out.println("this is DellCpu");
    }
}

DellMouse

/**
 * @author yangqiang
 * @date 2021-07-16 20:40
 */
public class DellMouse implements Mouse {
    @Override
    public void operation() {
        System.out.println("this is DellMouse");
    }
}

DellKeyboard

/**
 * @author yangqiang
 * @date 2021-07-16 19:56
 */
public class DellKeyboard implements Keyboard {
    @Override
    public void operation() {
        System.out.println("this is DellKeyboard");
    }
}

测试类

/**
 * @author yangqiang
 * @date 2021-07-16 17:42
 */
public class ComputerTest {
    public static void main(String[] args) {
        DellFactory dellFactory = new DellFactory();
        dellFactory.getCpu().operation();
        dellFactory.getKeyboard().operation();
        dellFactory.getMouse().operation();
    }
}

扩展

比如这时我们需要增加一个惠普电脑产品只需要新增一个HpFactory

HpFactory

/**
 * @author yangqiang
 * @date 2021-07-16 19:22
 */
public class HpFactory implements Computer {
​
    @Override
    public Cpu getCpu() {
        return new DellCpu();
    }
​
    @Override
    public Keyboard getKeyboard() {
        return new DellKeyboard();
    }
​
    @Override
    public Mouse getMouse() {
        return new DellMouse();
    }
}

HpCpu

/**
 * @author yangqiang
 * @date 2021-07-16 19:34
 */
public class HpCpu implements Cpu {
    @Override
    public void operation() {
        System.out.println("this is HpCpu");
    }
}

HpMouse

/**
 * @author yangqiang
 * @date 2021-07-16 20:40
 */
public class HpMouse implements Mouse {
    @Override
    public void operation() {
        System.out.println("this is HpMouse");
    }
}

HpKeyboard

/**
 * @author yangqiang
 * @date 2021-07-16 19:56
 */
public class HpKeyboard implements Keyboard {
    @Override
    public void operation() {
        System.out.println("this is HpKeyboard");
    }
}

实例的UML图也就变成了这样

image.png

抽象工厂模式是工厂方法模式的进一步延伸, 由于它提供了功能更为强大的工厂类并且具备较好的可扩展性, 在软件开发中得以广泛应用, 尤其是在一些框架和API类库的设计中, 例如在Java语言的AWT( 抽象窗口工具包) 中就使用了抽象工厂模式, 它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。 抽象工厂模式也是在软件开发中最常用的设计模式之一。

抽象工厂模式在Spring中的应用

package org.springframework.aop.framework;
​
public interface AopProxyFactory {
​
   /**
    * Create an {@link AopProxy} for the given AOP configuration.
    * @param config the AOP configuration in the form of an
    * AdvisedSupport object
    * @return the corresponding AOP proxy
    * @throws AopConfigException if the configuration is invalid
    */
   AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
​
}

AopProxyFactory 是个抽象的生产AopProxy的工厂 AopProxy 可以是由JDK动态代理 也可以是由CGLIB动态代理

所以AopProxy 也是一个抽象的类。具体的代理对象由AopProxyFactory的子类工厂DefaultAopProxyFactory去进行实现生成 当然最好是每个 不同的代理对象有其对应的代理工厂

​
​
package org.springframework.aop.framework;
​
import java.io.Serializable;
import java.lang.reflect.Proxy;
​
import org.springframework.aop.SpringProxy;
import org.springframework.core.NativeDetector;
import org.springframework.util.ClassUtils;
​
​
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
​
   private static final long serialVersionUID = 7930414337282325166L;
​
​
   @Override
   public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
      /*
       * 下面的三个条件简单分析一下:
       *
       *   条件1:config.isOptimize() - 是否需要优化,
       *   条件2:config.isProxyTargetClass() - 检测 proxyTargetClass 的值,
       *         前面的代码会设置这个值
       *   <aop:aspectj-autoproxy proxy-target-class="true"> 通过这种方式设置
       *   条件3:hasNoUserSuppliedProxyInterfaces(config)
       *         - 目标 bean 是否实现了接口
       */
​
      if (!NativeDetector.inNativeImage() &&
            (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
         Class<?> targetClass = config.getTargetClass();
         if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                  "Either an interface or a target is required for proxy creation.");
         }
         if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
         }
         // 创建 CGLIB 代理,ObjenesisCglibAopProxy 继承自 CglibAopProxy
         return new ObjenesisCglibAopProxy(config);
      }
      else {
         // 创建 JDK 动态代理
         return new JdkDynamicAopProxy(config);
      }
   }
​
   /**
    * Determine whether the supplied {@link AdvisedSupport} has only the
    * {@link org.springframework.aop.SpringProxy} interface specified
    * (or no proxy interfaces specified at all).
    */
   private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
      Class<?>[] ifcs = config.getProxiedInterfaces();
      return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
   }
​
}

优点

  • 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。 由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
  • 当一个族中的多个对象被设计成一起工作时, 它能够保证客户端始终只使用同一个族中的对象。
  • 增加新的具体工厂很方便,比如增加一个HpFactory,无须修改已有系统,符合“开闭原则”。
缺点
  • 增加新的抽象产品时会比较麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便。比如我要增加一个电源抽象产品。这时候需要改动的地方有。Computer(抽象工厂),DellFactory(具体工厂),以及新增一个具体产品类(PowerSupply),这种情况不符合开闭原则,对于产品类的扩展不友好。
适用场景
  • 一个系统不应当依赖于具体类实例如何被创建、 组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
  • 系统中有多于一个的实现工厂 比如DellFactory和HpFactory。如果是一个工厂的话可能使用工厂方法模式会比较好用。多于一个时在抽象工厂模式下 扩展会更加方便。
  • 属于同一个工厂下的具体产品一般都会一起使用。比如说DellFactory生成的DellMouse,DellCpu,DellKeyboard。都会在同一个Computer中体现出来。这个约束需要在系统的设计中体现出来
  • 系统结构设计稳定。一旦结构确定后。不会增加新的产品类型。因为抽象工厂模式对于新的产品类型扩展不是很友好。