前言
工厂模式(Factory Design Pattern)也是一种比较常用的创建型模式,它提供了一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,而是通过使用一个共同的接口来指定新创建的对象。
工厂模式可以细分为三种类型:简单工厂、工厂方法和抽象工厂,简单工厂、工厂方法在实际的项目中比较常用,而工厂方法在实际的项目中相对不常用。
简单工厂(Simple Factory)
什么是简单工厂模式,我们通过一个例子来解释一下。
1. 创建一个动物接口(Animal)
/**
* @Author:
* @Description: 动物接口
*/
public interface Animal {
void eat();
}
2. 狗狗类(Dog)实现动物接口
/**
* @Author:
* @Description: 狗类
*/
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗狗吃骨头");
}
}
3. 猫咪类(Cat)实现动物接口
/**
* @Author:
* @Description: 猫咪类
*/
public class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫咪吃鱼干");
}
}
4. 动物工厂类(AnimalFactory)
/**
* @Author:
* @Description: 动物工厂类(第一种实现)
*/
public class AnimalFactory {
public Animal createAnimal(String animalType) {
Animal animal = null;
if ("dog".equals(animalType)) {
animal = new Dog();
} else if ("cat".equals(animalType)) {
animal = new Cat();
}
return animal;
}
}
5. 测试
/**
* @Author:
* @Description: 测试
*/
public class Test {
public static void main(String[] args) {
AnimalFactory animalFactory = new AnimalFactory();
Animal dog = animalFactory.createAnimal("dog");
Animal cat = animalFactory.createAnimal("cat");
dog.eat();
cat.eat();
}
}
结果如下:
狗狗吃骨头
猫咪吃鱼干
在上面的代码实现中,我们每次调用 AnimalFactory 的 createAnimal() 方法的时候,都要创建一个新的 animal。实际上,如果 animal 可以复用,为了节省内存和对象创建的时间,我们可以将 animal 事先创建好缓存起来。当调用 createAnimal() 方法的时候,我们从缓存中取出 animal 对象直接使用。
这有点类似于单例模式和简单工厂模式的结合,具体代码实现如下所示:
/**
* @Author:
* @Description: 动物工厂类(第二种实现)
*/
public class AnimalFactory {
private static final Map<String, Animal> cachedAnimals = new HashMap<>();
static {
cachedAnimals.put("dog", new Dog());
cachedAnimals.put("cat", new Cat());
}
public Animal createAnimal(String animalType) {
return cachedAnimals.get(animalType);
}
}
对于上面两种简单工厂模式的实现方法,如果我们要添加新的 animal,那势必要改动到 AnimalFactory 的代码,是否违反开闭原则呐?实际上,如果不是频繁的添加新的 animal,只是偶尔修改一下 AnimalFactory 的代码,稍微不符合开闭原则也是可以接受的。
在第一种实现例子中, AnimalFactory 类中 if 只有两个分支,可以直接这样写,但如果有很多个 if 分支判断逻辑,就要考虑使用多态或设计模式来代替 if 分支判断逻辑了。
工厂方法(Factory Method)
如果我们非要将 AnimalFactory 类中的 if 分支逻辑去掉,比较经典的处理方法就是利用多态。按照多态的实现思路,对上面的代码进行重构。
/**
* @Author:
* @Description: 动物工厂接口
*/
public interface IAnimalFactory {
Animal createAnimal();
}
/**
* @Author:
* @Description: 狗狗工厂
*/
public class DogFactory implements IAnimalFactory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
/**
* @Author:
* @Description: 猫咪工厂
*/
public class CatFactory implements IAnimalFactory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
实际上,这就是工厂方法模式的典型代码实现。当我们新增一种 animal 的时候,只需要新增一个实现了 IAnimalFactory 接口的 Factory 类即可。所以,工厂方法模式比起简单工厂模式更加符合开闭原则。
从上面的实现来看,一切看起来都挺完美,但是实际上依旧存在问题,问题在于这些工厂类的使用上。
/**
* @Author:
* @Description:
*/
public class Test {
public static void main(String[] args) {
Animal animal = null;
if ("dog".equals(getAnimalType())) {
DogFactory dogFactory = new DogFactory();
animal = dogFactory.createAnimal();
} else if ("cat".equals(getAnimalType())) {
CatFactory catFactory = new CatFactory();
animal = catFactory.createAnimal();
}
animal.eat();
}
public static String getAnimalType() {
return "dog";
}
}
看上面的代码,使用工厂类的时候依旧会出现 if 分支语句,感觉设计好像变得更复杂了,要解决这个问题,我们可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象。
其中,AnimalFactoryMap 类是创建工厂对象的工厂类,getAnimalFactory() 方法返回的是缓存好的单例工厂对象。
/**
* @Author:
* @Description: 工厂的工厂,不需要每次都创建新的工厂类对象
*/
public class AnimalFactoryMap {
private static final Map<String, IAnimalFactory> cachedFactories = new HashMap<>();
static {
cachedFactories.put("dog", new DogFactory());
cachedFactories.put("cat", new CatFactory());
}
public static IAnimalFactory getAnimalFactory(String animalType) {
return cachedFactories.get(animalType.toLowerCase());
}
}
使用工厂类:
/**
* @Author:
* @Description:
*/
public class Test {
public static void main(String[] args) {
IAnimalFactory animalFactory = AnimalFactoryMap.getAnimalFactory(getAnimalType());
Animal animal = animalFactory.createAnimal();
animal.eat();
}
public static String getAnimalType() {
return "cat";
}
}
这样,当我们需要添加新的动物比如鸟的时候,我们只需要创建新的 Bird 类和 BirdFactory 类,并且在 AnimalFactoryMap 类中,将新的 BirdFactory 对象添加到 cachedFactories 中即可。代码改动不多,基本符合开闭原则。
实际上,如果每个 Factory 类只是做简单的 new 操作,功能非常单薄,也没必要设计成独立的类,所以在这样的应用场景中,简单工厂模式比工厂方法模式更加合适。也就是当对象的创建逻辑比较复杂时候,我们推荐使用工厂方法模式。
抽象工厂
抽象工模式的应用场景比较特殊,没有前两种常用。上面的例子已经不再合适了,可能需要再重新写一篇抽象工厂模式的了。
注意
工厂模式作为一种创建型模式,在任何需要生成复杂对象的地方,都可以使用。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。