一:基础案例

实现一个咖啡店点一种咖啡(美式咖啡、拿铁咖啡等)。

创建型设计模式-工厂方法模式_工厂方法

public abstract class Coffee {
    public abstract String getName();

    public void addSugar() {
        System.out.println("Coffee 加糖");
    }

    public void addMilk() {
        System.out.println("Coffee 加奶");
    }
}
public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}
public class LatteCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}
public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        Coffee coffee = null;
        if ("american".equals(type)) {
            coffee = new AmericanCoffee();
        } else if ("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("咖啡不存在");
        }

        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}
public class Client {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("latte");
        System.out.println(coffee.getName());
    }
}

缺点:如果咖啡店新增一种咖啡,我们就必须修改orderCoffee(type)方法,增加 else if 代码,这就违反了设计原则中的开闭原则(对新增开放,对修改关闭)。

二:基础案例优化1 - 简单工厂

基础案例将创建Coffee对象的一堆代码写在具体的业务逻辑orderCoffee()里,如果还有其它业务逻辑也需要相同的功能就不能达到复用,所以需要将创建实体的这片代码抽成一个公共方法,甚至将这个公共方法放在一个单独的类里。

创建型设计模式-工厂方法模式_java_02

public class SimpleCoffeeFactory {

    public Coffee createCoffee(String type) {
        Coffee coffee = null;
        if ("american".equals(type)) {
            coffee = new AmericanCoffee();
        } else if ("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("咖啡不存在");
        }

        return coffee;
    }
}
public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        // 调用功能的方法来获取Coffee对象
        SimpleCoffeeFactory coffeeFactory = new SimpleCoffeeFactory();
        Coffee coffee = coffeeFactory.createCoffee(type);

        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}

人们将这种创建对象的方法放在一个单独的类中(类名通常以Factory作为后缀)的做法叫做“简单工厂模式”,这种实现方式并不算一种设计模式,也没有解决设计原则-开闭原则,只是实际工作中这种做法比较多而已,这种做法的好处就是将创建对象的这一片代码抽离了出来,减少了客户端代码的代码量,使得客户端的代码更加内聚紧凑。该做法虽然没有解决开闭原则,但是比原来的做法要稍微好些。

三:基础案例优化2 - 静态工厂

基础案例优化1在调用SimpleCoffeeFactory#createCoffee(type) 时需要先把SimpleCoffeeFactory对象先new出来,在工作中可以将createCoffee(type) 方法变成静态方法 static , 这样在调用创建方法的时候可以直接通过类名来调用,。

public class SimpleCoffeeFactory {

    public static Coffee createCoffee(String type) {
        Coffee coffee = null;
        if ("american".equals(type)) {
            coffee = new AmericanCoffee();
        } else if ("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("咖啡不存在");
        }

        return coffee;
    }
}
public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        // 静态方法,直接调用
        Coffee coffee = SimpleCoffeeFactory.createCoffee(type);

        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}

四:工厂方法设计模式

简单工厂是一个方法创建所有Coffee类的对象,而工厂方法设计是为每个实体都对应一个相对应的工厂,每个工厂只生产对应的实体,每个工厂都遵守相同的接口。

创建型设计模式-工厂方法模式_java_03

public interface CoffeeFactory {
    Coffee createCoffee();
}
public class AmericanCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}
public class LatteCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}
public class CoffeeStore {

    private CoffeeFactory coffeeFactory;

    public CoffeeStore(CoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee() {
    	// 通过多态特性分别调用自己的工厂的创建Coffee的方法获取具体的咖啡。
        Coffee coffee = coffeeFactory.createCoffee();
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}
public class Client {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        // 传参由原来的String type 改为 多态工厂
        CoffeeFactory coffeeFactory = new LatteCoffeeFactory();
        coffeeStore.setCoffeeFactory(coffeeFactory);
        
        Coffee coffee = coffeeStore.orderCoffee();
        System.out.println(coffee);
    }
}

4.1 工厂方法中的概念

  • 抽象工厂(CoffeeFactory)
  • 具体工厂(LatteCoffeeFactory)
  • 抽象商品类(Coffee)
  • 具体产品类(LatteCoffee)

4.2 工厂方法设计模式套路

  1. 提供一个工厂接口,接口方法为创建实体。
  2. 为每个实体都提供一个与之对应的工厂类并实现工厂接口的创建实体的方法。
  3. 业务类中提供一个工厂接口的引用和set方法,业务方法通过调用工厂接口中的创建对象方法来获取对象。
  4. 客户端在使用业务类时需要设置具体的工厂对象。

4.3 工厂方法设计模式是如何遵守开闭原则的

工厂方法设计模式就是利用接口中的多态属性来消除 else if 代码,从而实现开闭原则。

4.4 工厂方法设计模式的优缺点

  • 优点:工厂方法不但遵守了开闭原则,而且将创建对象的代码进一步细化分离,使得代码更加内聚。
  • 缺点:每新增一种咖啡就要多新增与之对应的工厂类,可能会引起类爆炸(类过多)。

4.5 为什么要遵守开闭原则?

因为修改代码会影响整个点咖啡的功能,一旦修改的代码出现了问题则影响整个点咖啡的功能。
如果是不修改只增加代码,即使增加的代码有问题也只会影响新增加的功能,不影响原来的功能,这就是降低了出现问题的可能性。

4.6 工厂方法模式什么场景下使用?

工厂方法设计模式一般只适用于同一类对象的产生,即每个具体的对象都有相同的父类。因为工厂中的方法的返回值是父类(Coffee)。

public interface CoffeeFactory {
    Coffee createCoffee();
}

五:工厂方法设计模式 - 新增一种咖啡

如果咖啡店要新增一种新的咖啡如(卡布奇诺咖啡)我们只需要新增一种具体的咖啡类CappuccinoCoffee,然后再新增对应的工厂 CappuccinoCoffeeFactory,然后客户端只需要修改咖啡工厂即可,整个过程我们只是新增了代码,并没有修改已有的代码(客户端代码不算)。

public class CappuccinoCoffee extends Coffee {
    @Override
    public String getName() {
        return "卡布奇诺咖啡";
    }
}
public class CappuccinoCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new CappuccinoCoffee();
    }
}
public class Client {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        // 客户端只需要修改咖啡工厂即可
        CoffeeFactory coffeeFactory = new CappuccinoCoffeeFactory();
        coffeeStore.setCoffeeFactory(coffeeFactory);

        Coffee coffee = coffeeStore.orderCoffee();
        System.out.println(coffee);
    }
}

六:JDK中工厂方法的使用

  • 抽象工厂(Collection):相当于CoffeeFactory,iterator()相当于createCoffee()
  • 具体工厂(ArrayList): 相当于LatteCoffeeFactory
  • 抽象商品类(Iterator):相当于Coffee
  • 具体产品类(ArrayList$Itr):相当于LatteCoffee
// 抽象工厂
public interface Collection<E> extends Iterable<E> {
	// 创建对象方法
	Iterator<E> iterator();

	boolean isEmpty();
}

// 具体工厂 ArrayList implements List implements Collection
public class ArrayList<E> extends Collection {
	public Iterator<E> iterator() {
        return new Itr();
    }

	public boolean isEmpty() {
        return size == 0;
    }

	// 具体产品(私有内部类)
	private class Itr implements Iterator<E> {

	}
}


// 抽象产品
public interface Iterator<E> {
	
}
public static void main(String[] args) {
    // 咖啡工厂方法示例
    CoffeeFactory coffeeFactory = new LatteCoffeeFactory();
    Coffee coffee = coffeeFactory.createCoffee();
    System.out.println(coffee);
    
    // JDK集合工厂方法示例
    List<String> list = new ArrayList<>();
    Iterator<String> iterator = list.iterator();
    System.out.println(iterator);

    // 工厂中的其它方法
    boolean isEmpty = list.isEmpty();
}

从JDK集合工厂方法模式中可以看出工厂方法模式不止只有一个创建对象的方法,还有一些其它相关的方法(像Collection#isEmpty()用于判断集合是否为空)。所以我们理解工厂方法模式理解的也不能太狭隘了。


  • DateFormate类中的getInstance()方法也是使用的工厂方法模式;
  • Calendar类中的getInstance()方法也是使用的工厂方法模式;