设计模式学习笔记(6)工厂方法

本文实例代码:https://github.com/JamesZBL/java_design_patterns

工厂方法(Factory Method)模式,又叫做虚拟构造(Virtual Constructor)模式或多态工厂(Polymorphic Factory)模式。工厂方法的特点是定义一个用于创建对象的接口, 让子类决定实例化哪一个类。 工厂方法使一个类的实例化延迟到其子类。

实例

这次以顾客点餐为例,假设有两个厨师,一个只会做中餐,另一个只会做西餐,餐品分为熟食和生食两类。顾客需要顾客需要根据自己的口味来选择对应的厨师并告知其需要熟食还是生食,厨师根据顾客的口味来进行烹制。

厨师的职责就是烹制食物,所以定义一个厨师接口

Cook.java

public interface Cook {
  Food cookFood(FoodType foodType);
}

现在简单的将食物赋予一个表示“冷或热”的属性,用枚举类型表示

FoodType.java

public enum FoodType {
  HOT("热的"), COLD("凉的");
  private String name;
  FoodType(String foodType) {
    this.name = foodType;
  }
  public String getName() {
    return name;
  }
}

定义食物接口

Food.java

public interface Food {
  FoodType getFoodType();
}

食物有中餐和西餐之分,分别定义两个食物接口的实现类

ChineseFood.java

public class ChineseFood implements Food {

  private FoodType foodType;

  public ChineseFood(FoodType foodType) {
    this.foodType = foodType;
  }

  @Override
  public FoodType getFoodType() {
    return foodType;
  }

  @Override
  public String toString() {
    return foodType.getName() + "中餐";
  }
}

WesternFood.java

public class WesternFood implements Food {

  private FoodType foodType;

  public WesternFood(FoodType foodType) {
    this.foodType = foodType;
  }

  @Override
  public FoodType getFoodType() {
    return foodType;
  }

  @Override
  public String toString() {
    return foodType.getName() + "西餐";
  }
}

中餐厨师和西餐厨师分别烹制中餐和西餐,定义两个厨师接口的实现类

ChineseCook.java

public class ChineseCook implements Cook {
  public Food cookFood(FoodType foodType) {
    return new ChineseFood(foodType);
  }
}

WesternCook.java

public class WesternCook implements Cook {
  public Food cookFood(FoodType foodType) {
    return new WesternFood(foodType);
  }
}

“食物”产品的生产现在交给了“厨师工厂”来实现:

Cook cook1 = new WesternCook();
Cook cook2 = new ChineseCook();
Food food1 = cook1.cookFood(FoodType.COLD);
Food food2 = cook2.cookFood(FoodType.HOT);

本例只是单纯的演示工厂方法,因为对于这几个类而言,显然使用工厂方法模式比直接使用 new 关键字调用构造方法来说要复杂多了,所以简单对象的创建无需引入工厂模式,从而避免给系统带来更高的复杂度。

实际应用

Java 的集合是一套优秀的数据结构设计,大部分集合类型都实现 java.util.Collection 接口,这个接口的父接口 Iterable 接口规定了所有的 Java 集合都必须提供一个 iterator() 方法,返还一个Iterator 类型的对象:

java.lang.Iterable.java

public interface Iterable<E> {
    // ...
    Iterator<E> iterator();
    // ...
}

ArrayList 是我们常用的一个 Collection 接口实现类,其 iterator() 方法实现如下:

java.util.ArrayList.java

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    // ...
    // 返回一个 Iterator 对象,工厂方法的实现
    public Iterator<E> iterator() {
        return new Itr();
    }
    private class Itr implements Iterator<E> {
    // ...
    }
    // ...
}

不难看出,ArrayList 中的 iterator() 方法是具体工厂类的工厂方法,而 Collection 就是一个工厂接口。

总结

在基于类的设计中,工厂方法模式通常作为创建模式来使用。它使用工厂方法来处理创建对象的过程,无需指定创建对象的确切类型。客户端通过调用工厂方法来创建对象,这里的方法是在接口中指定的,或是由子类实现的,或是由基类实现,或者通过子类进行方法覆盖,从头至尾无需调用具体类的构造方法。

工厂方法模式就是为了完全满足“开闭原则”,在上文点餐的例子中,当增加食物类型的时候,无需改动现有的代码,只要增加一个能够完成新类型食物烹饪工作的厨师类即可。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.