工厂模式

案例

假设我开了一个工厂来组装特斯拉汽车,组装汽车需要“安装引擎-装上盖-装车门”这三道工序,目前这些工序都是由工人手工完成的,那么我写的代码可能如下

创建一个工人类,实现三道工序的操作:

public class Worker {
 
    public void installEngine(Tesla tesla) {
        // work work
    }
    
    public void installTopBox(Tesla tesla) {
        // work work
    }
    
    public void installDoor(Tesla tesla) {
        // work work
    }
}

然后在主程序中创建工人实例,并执行这三个方法

public void installCar() {
    Worker zhangSan = new Worker();
    Tesla tesla01 = new Tesla();
    zhangSan.installEngine(tesla01);
    check(tesla01);
    zhangSan.installTopBox(tesla01);
    check(tesla01);
    zhangSan.installDoor(tesla01);
    check(tesla01);
}
 
public void check(Tesla tesla) {
    // 检查汽车安装是否有问题
}
 

然后有一天,你引入了一个机器人,可以代替工人来进行汽车的组装操作。工人下岗了,那么你就需要在 installCar 方法中将和工人有关的相关代码全部删除,然后添加机器人的代码。

第二周,工人联合抗议,迫于舆论压力,你不得不放弃把工人全部裁光的想法,决定让人工组装与机器组装并行。于是你又需要将你才删掉的代码重新加回来。

在这一过程中,你频繁地修改 installCar 这个方法,极容易将这个方法改出问题,那么需要如何优化设计呢?

创建产品接口

在上面代码中,每做完一步就要进行一次检查操作,所以无论是人工操作还是机器操作,操作步骤都应该规范为这三步,不然检查代码可能就可能会出现问题。所以需要创建一个产品的接口来声明这些方法:

public interface Product {
    void installEngine();
    void installTopBox();
    void installDoor();   
}

让工人和机器人都来实现这个 Product 接口。

public void installCar() {
    Product product = new Worker();
    Tesla tesla01 = new Tesla();
    product.installEngine(tesla01);
    check(tesla01);
    product.installTopBox(tesla01);
    check(tesla01);
    product.installDoor(tesla01);
    check(tesla01);
}

虽说规范了这三道工序,但是仍不能保证工人和机器人能乖乖按照这个流程工作。比如工人又造反了,你又要给工人加一步“休息”的操作。所以最好把整个过程都封装到一个方法里面:

public void installCar() {
    Product product = new Worker();
    product.install();
}

创建工厂

安装的问题基本上解决了,接下来是初始化的问题。New 一个工人和 new 一个机器人的操作可能不同,首先构造函数传入的参数可能不同,然后比如说机器只有一个,只能使用单例模式;工人的数量有限,要从资源池里去获取等等。所以创建实例的操作最好也被隐藏。这个时候我们就使用工厂来专门负责创建的操作。这也是依赖倒置原则的体现:高层次

但这只是把创建代码换个地方放而已,并没有根本解决问题。所以我们把工厂弄成一个抽象类,具体的创建操作由各个子类去实现:

public abstract class ProductFactory {
    
    public void install() {
        Product product = createProduct();
        product.installEngine();
        product.installTopBox();
        product.installDoor();       
    }
    
    public abstract Product createProduct();
}

这里把核心业务逻辑 install() 放在抽象类里。如果出现特例,比如工人需要休息,就可以在工人的 Factory 中覆写 install() 方法,实现自己的逻辑。

installCar 方法中,调用对应的工厂方法即可:

public void installCar() {
    ProductFactory factory = new WorkerFactory();
    factory.install();
}

使用接口的默认方法实现工厂模式

import java.util.*;
 
interface Pet {}
class Dog implements Pet {}
class Cat implements Pet {}
 
interface Person {
    Pet getPet();
    
    default void play() {
        System.out.println("playing with " + getPet());
    }
}
 
class DogPerson implements Person {
    private Dog dog = new Dog();
 
    @Override
    public Pet getPet() {
        return dog;
    }
}
 
class CatLover implements Person {
    private Cat cat = new Cat();
 
    @Override
    public Pet getPet() {
        return cat;
    }
}
 
public class Sample {
 
    public static void call(Person person) {
        person.play();
    }
 
    public static void main(String[] args) {
        call(new DogPerson());
        call(new CatLover());
    }
 
}