What
当创建逻辑比较复杂时,我们就可以考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用分离开来。工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂,其中,简单工厂和工厂方法比较常用,抽象工厂在实际项目种并不常用。
Why
为什么需要工厂模式呢?当创建对象的逻辑比较复杂时,如果将对象创建和使用放在一起,会导致代码的可维护性和可扩展性都很差。
那么,什么情况下可视为创建对象的逻辑比较复杂呢?大致可以总结为以下两种情况:
- 创建对象时需要动态的根据不同的类型创建不同的对象,即含有较多的if-else分支。此时,使用工厂模式将对象创建逻辑封装至工厂类中,使得对象创建过程对对象使用来说是透明的。
- 对象创建过程需要做很多初始化操作,此时可以使用工厂模式,将复杂的初始化操作封装到工厂类中。
借助工厂模式,可以达到以下效果:
封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
代码复用:创建代码抽离到独立的工厂类之后可以复用。
隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
How
接下来,我们通过一个简单需求,来介绍工厂模式的实现方法。
需求如下:
一台笔记本电脑由CPU、内存条、硬盘等硬件组成,而CPU、内存条、硬盘有不同的品牌。比如,常见的CPU有Intel、AMD、IBM等品牌。一台笔记本的生产其实就是将生产好的CPU、内存条、硬盘进行组装即可。同时,笔记本也有不同的品牌。现在,我们将笔记本的生产过程进行抽象,翻译成代码。
首先,我们先以CPU的生产过程来介绍简单工厂和工厂方法两种方式。这里,我创建了CPU抽象类(Cpu),以及不同的品牌CPU类(IntelCpu、AmdCpu、IbmCpu)。Cpu类含有两个成员变量:brand和useTech,分别代表品牌和使用技术(仅做简单示例);对于CPU生产过程,我们这里并没有展开去实现,只是打印说明了一下,关键还是想让大家重点关注工厂模式的实现方法。
public abstract class Cpu {
private String brand;
private String useTech;
public Cpu(String brand, String useTech) {
this.brand = brand;
this.useTech = useTech;
}
public abstract void create();
}
public class IntelCpu extends Cpu {
public IntelCpu() {
super("Intel", "Intel use intel's unique technology");
// Omitting complex initialization logic
// ...
}
@Override
public void create() {
System.out.println("Intel CPU is creating");
// Omitting complex creating logic
// ...
System.out.println("Intel CPU is created finished");
}
}
// AmdCpu、IbmCpu代码和IntelCpu结构一致,因此省略
1. 简单工厂
简单工厂实现其实很简单,就是根据传入类型返回不同的对象。
public class CpuFactory {
public static Cpu getCpuCreator(String brand) {
Cpu cpu = null;
if (brand == null) {
throw new IllegalArgumentException("brand can not be empty");
}
if ("AMD".equals(brand)) {
cpu = new AmdCpu();
} else if ("Intel".equals(brand)) {
cpu = new IntelCpu();
} else if ("IBM".equals(brand)) {
cpu = new IbmCpu();
} else {
throw new IllegalStateException(String.format("The brand of %s does not exist", brand));
}
return cpu;
}
}
那么,笔记本的组装过程翻译为代码如下:
public class Laptop {
private Cpu cpu;
private Memory memory;
public Laptop() {
}
public void create(String cpuBrand, String memoryBrand) {
System.out.println("Laptop is creating");
cpu = CpuFactory.getCpuCreator(cpuBrand);
cpu.create();
System.out.println("Laptop is created finished");
}
}
在上面的代码实现中,我们每次调用 CpuFactory 的 getCpuCreator() 的时候,都要创建一个新的 cpu 对象。实际上,如果 cpu 可以复用,为了节省内存和对象创建的时间,我们可以将 cpu 对象事先创建好缓存起来。当调用 getCpuCreator() 函数的时候,我们从缓存中取出 cpu 对象直接使用。实现代码如下:
public class CpuFactory {
private static final Map<String, Cpu> cachedCreators = new HashMap<>();
static {
cachedCreators.put("AMD", new AmdCpu());
cachedCreators.put("Intel", new IntelCpu());
cachedCreators.put("IBM", new IbmCpu());
}
public static Cpu getCpuCreator(String brand) {
if (brand == null || brand.isEmpty()) {
throw new IllegalArgumentException("brand can not be empty");
}
if (cachedCreators.containsKey(brand)) {
return cachedCreators.get(brand);
}
throw new IllegalStateException(String.format("The brand of %s does not exist", brand));
}
}
简单工厂模式的这两种实现方式非常简单,但如果要添加新的cpu品牌,必须得更改CpuFactory类;但是如果不需要频繁添加的话,这样实现也没问题,并不违反开闭原则。但如果需要频繁更改的话,简单工厂模式就要升级下了,这就是接下来要讲的工厂方法模式。
2. 工厂方法
工厂方法模式可以解决上述提到的问题,它为每一个cpu品牌都创建一个工厂类,然后用一个Map类将这些工厂类缓存起来,根据传入的类型值返回对应的工厂对象,最后再用该工厂去创建对象。实现方式如下:
public interface ICpuFactory {
Cpu getCpuCreator();
}
public class AmdCpuFactory implements ICpuFactory {
@Override
public Cpu getCpuCreator() {
return new AmdCpu();
}
}
public class IntelCpuFactory implements ICpuFactory {
@Override
public Cpu getCpuCreator() {
return new IntelCpu();
}
}
public class IbmCpuFactory implements ICpuFactory {
@Override
public Cpu getCpuCreator() {
return new IbmCpu();
}
}
public class CpuFactoryMap {
private static final Map<String, ICpuFactory> cachedCreatorFactories = new HashMap<>();
static {
cachedCreatorFactories.put("AMD", new AmdCpuFactory());
cachedCreatorFactories.put("Intel", new IntelCpuFactory());
cachedCreatorFactories.put("IBM", new IbmCpuFactory());
}
public static ICpuFactory getCpuCreatorFactory(String brand) {
if (brand == null || brand.isEmpty()) {
throw new IllegalArgumentException("brand can not be empty");
}
if (cachedCreatorFactories.containsKey(brand)) {
return cachedCreatorFactories.get(brand);
}
throw new IllegalStateException(String.format("The brand of %s does not exist", brand));
}
}
此时,笔记本的组装代码如下:
public class Laptop {
private Cpu cpu;
private Memory memory;
public Laptop() {
}
public void createByFactoryMethod(String cpuBrand, String memoryBrand) {
System.out.println("------------ factory method ------------");
System.out.println("Laptop is creating");
ICpuFactory cpuCreatorFactory = CpuFactoryMap.getCpuCreatorFactory(cpuBrand);
cpuCreatorFactory.getCpuCreator().create();
System.out.println("Laptop is created finished");
System.out.println();
}
}
此时当我们需要添加新的CPU品牌的时候,我们只需要创建新的 CPU 类和 CPU factory 类,并且在 CpuFactoryMap 类中,将新的 CPU factory 对象添加到 cachedFactories 中即可。代码的改动非常少,基本上符合开闭原则。
3. 抽象工厂
只对于CPU这一个组件,我们创建了3个工厂类。如果对于Memory、Driver等组件都和CPU一样,对每个组件都搞n个工厂类,这会导致类的个数非常多,增加了维护的成本;因此,我们可以只针对笔记本这个类建一个工厂,把所有组件的创建对象都放到这个工厂里面。这样的话,不同品牌的笔记本对应不同的笔记本工厂。实现方式如下:
public interface LaptopFactory {
Cpu getCpuCreator();
Memory getMemoryCreator();
}
public class MacLaptopFactory implements LaptopFactory {
@Override
public Cpu getCpuCreator() {
return new IntelCpu();
}
@Override
public Memory getMemoryCreator() {
return new KingstonMemory();
}
}
public class WindowsLaptopFactory implements LaptopFactory {
@Override
public Cpu getCpuCreator() {
return new AmdCpu();
}
@Override
public Memory getMemoryCreator() {
return new CorsairMemory();
}
}
public class LaptopFactoryMap {
private static final Map<String, LaptopFactory> cachedLaptopFactories = new HashMap<>();
static {
cachedLaptopFactories.put("Mac", new MacLaptopFactory());
cachedLaptopFactories.put("Windows", new WindowsLaptopFactory());
}
public static LaptopFactory getLaptopFactory(String laptopBrand){
if (laptopBrand == null || laptopBrand.isEmpty()) {
throw new IllegalArgumentException("brand can not be empty");
}
if (cachedLaptopFactories.containsKey(laptopBrand)) {
return cachedLaptopFactories.get(laptopBrand);
}
throw new IllegalStateException(String.format("The laptopBrand of %s does not exist", laptopBrand));
}
}
此时,电脑组装代码如下:
public class Laptop {
private Cpu cpu;
private Memory memory;
public Laptop() {
}
public void createByAbstractFactory(String laptopBrand) {
System.out.println("------------ abstract factory ------------");
System.out.println(String.format("%s Laptop is creating", laptopBrand));
LaptopFactory laptopFactory = LaptopFactoryMap.getLaptopFactory(laptopBrand);
laptopFactory.getCpuCreator().create();
laptopFactory.getMemoryCreator().create();
System.out.println(String.format("%s Laptop is created finished", laptopBrand));
System.out.println();
}
}
抽象工厂的核心就是一个工厂类里面支持同时创建多个对象,这样就可以有效地减少工厂类的个数。
代码地址
写在最后
如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~