我们有一个 Pizza 店,会根据顾客的要求制作不同类型的 Pizza。目前有三种不同类型的 Pizza:「Cheese & Greek & Pepperoni」Pizza
制作每种类型的 Pizza 时,都会经历 4 个步骤:前期准备「Prepare」、烘焙「Bake」、切「Cut」、装盒「Box」
下面先给出对应的类图:
接着给出代码:
// 抽象类 Pizza
abstract public class Pizza {
String name;
public String getName() {
return name;
}
// 准备
public void prepare() {
System.out.println("准备 " + name);
}
// 烘焙
public void bake() {
System.out.println("烘焙 " + name);
}
// 切
public void cut() {
System.out.println("切 " + name);
}
// 装盒
public void box() {
System.out.println("装盒 " + name);
}
}
// 抽象类 Pizza 的一个实现类 CheesePizza (其它同理)
public class CheesePizza extends Pizza {
public CheesePizza() {
this.name = "Cheese Pizza";
}
}
// PizzaStore 类
public class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("pepperoni")) {
pizza = new Pepperoni();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 测试代码
public static void main(String[] args) {
PizzaStore pizzaStore = new PizzaStore();
System.out.println("我们准备了一个 Cheese Pizza !");
Pizza pizza = pizzaStore.orderPizza("cheese");
}
}
问题:当后续需要添加新的 Pizza 类型或者删去已有的 Pizza 类型,这时就需要修改已有代码的逻辑,违背了「开闭原则」
开闭原则:开放拓展,关闭修改!!
这里还有一个原则,把变化的部分抽离出来,使变化的部分独立客户端的使用
在这个例子里面,变化的部分就是每次需要根据不同类型制作不同的 Pizza,而不变的部分就是制作完 Pizza 后的四个步骤「准备、烘焙、切、装盒」
所以我们可以把「制作不同类型 Pizza」的部分抽离出来,放到工厂中,每次需要制作 Pizza 时,去工厂里取即可!
下面先给出对应的类图:
接着给出核心代码:
x// 简单工厂类
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("pepperoni")) {
pizza = new Pepperoni();
}
return pizza;
}
}
public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
// 从工厂中获得
Pizza pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
这样当需要添加新的 Pizza 类型或者删去已有的 Pizza 类型,只用在createPizza()
中 修改即可!!
可能有人会提问:这不就是把orderPizza()
中的判断逻辑移到了createPizza()
中吗??这样不就只是把问题移到了另一个对象中吗??
确实只是把判断逻辑移到了createPizza()
中,这样关闭了对orderPizza()
的修改
在现实中不仅仅只有orderPizza()
需要根据类型生成 Pizza,在PizzaShopClass
中也需要这样的功能,PizzaShopClass
也可以直接从工厂中获取 Pizza,而不需要在自己内部实现逻辑判断
如果不引入「简单工厂模式」,对于一个改变,可能会修改多处的代码,例如:orderPizza()
的逻辑判断部分、PizzaShopClass
中的逻辑判断部分等等
而引入「简单工厂模式」后,对于一个改变,只需要修改工厂中对应的逻辑判读部分即可!!
现在,我们在不同地区开了分店,为了迎合当地人的习惯,同一款在不同地区的口味也应该有所区别;也需要对『前期准备「Prepare」、烘焙「Bake」、切「Cut」、装盒「Box」』四个步骤进行有针对性的改进,即对于不同地区,相同的步骤需要改变
下面先给出对应的类图:
接着给出代码:
xxxxxxxxxx
// Pizza 抽象类
public abstract class Pizza {
public String name;
// 面团
public String dough;
// 酱
public String sauce;
// 浇头
public List<String> toppings = new ArrayList<>();
public void prepare() {
System.out.println("准备 " + name);
System.out.println("揉面团 ...");
System.out.println("添加酱汁 ...");
System.out.println("添加浇头:");
for (String topping : toppings) {
System.out.println(" " + topping);
}
}
public void bake() {
System.out.println("烘焙 25 分钟");
}
public void cut() {
System.out.println("把 pizza 切成三角形切片");
}
public void box() {
System.out.println("用官方的盒子包装");
}
public String getName() {
return name;
}
}
// 实现抽象类 Pizza (其他同理)
public class NYStyleCheesePizza extends Pizza {
// 根据不同地区不同风格,可以用不同的配料和制作细节
public NYStyleCheesePizza() {
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
// PizzaStore 抽象类
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 抽象方法
protected abstract Pizza createPizza(String type);
}
// 实现抽象类 PizzaStore (其他同理)
public class NYStylePizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (item.equals("veggie")) {
return new NYStyleVeggiePizza();
} else if (item.equals("clam")) {
return new NYStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new NYStylePepperoniPizza();
} else return null;
}
}
由此可以引出抽象方法模式的定义:
The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
解释:工厂方法模式定义了一个创建对象的接口 (在该例子中为抽象类Pizza
),让子类决定实例化哪个类 (在该例子中为NYStylePizzaStore
)。工厂方法让类的实例化延迟到了子类中
为了严谨,这里再给出「工厂方法模式」的通用类图:
Depend upon abstractions. Do not depend upon concrete classes.
解释:依赖抽象,不要依赖具体的类
当我们未使用任何设计模式时,PizzaStore
直接依赖具体的实现类,其依赖关系如下图所示:
当我们使用了「工厂方法模式」时,PizzaStore
依赖抽象类Pizza
;同时具体的 Pizza 也依赖抽象类Pizza
,其依赖关系如下图所示:
我们的设计尽可能的遵循「依赖倒置」原则,这样可以使系统实现低耦合!!
先看工厂方法模式中的NYStyleCheesePizza
类,dough、sauce、toppings
等配料都是经销商自己制作,为了节约成本,存在偷工减料的情况,严重影响品牌口碑
为了杜绝这种情况,需要把不同地区不同类型 Pizza 的配料掌握在总部手中,即由总部分发配料到经销商手中,这样可以实现配料统一
下面先给出对应的类图:
接着给出核心代码:
xxxxxxxxxx
// PizzaStore 同上
public class NYStylePizzaStore extends PizzaStore{
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
} else if (item.equals("veggie")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("New York Style Veggie Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
} else if (item.equals("pepperoni")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");
}
return pizza;
}
}
// 其他 (Sauce Cheese Clams) 同理
public interface Dough {
String toString();
}
// 其他同理
public class ThickCrustDough implements Dough {
public String toString() {
return "Thick Crust Dough";
}
}
// 配料抽象工厂
public interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
Veggies[] createVeggies();
Pepperoni createPepperoni();
Clams createClams();
}
// ChicagoPizzaIngredientFactory 同理
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
return new Veggies[]{ new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClams() {
return new FreshClams();
}
}
// 抽象类 Pizza
public abstract class Pizza {
public String name;
public Dough dough;
public Sauce sauce;
public Veggies veggies[];
public Cheese cheese;
public Pepperoni pepperoni;
public Clams clam;
protected Pizza() {
}
public abstract void prepare();
public void bake() {
System.out.println("Bake for 25 minutes at 350");
}
public void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
public void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
// code to print pizza here
}
}
为了严谨,这里再给出「工厂方法模式」的通用类图:
由此可以引出抽象方法模式的定义:
The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
解释:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
下面来梳理一下这两个模式的区别!!
核心不同点:「工厂方法模式」使用类创建对象;而「抽象工厂模式」使用对象创建对象
工厂方法模式通过继承的方式创建不同类型的对象。子类考虑具体的类型 (把类的实例化延迟到了子类中),而客户端只需要知道抽象的类型即可。且对于每一个子类,只能创建一种类型的对象,详情可见NYStylePizzaStore
;而抽象工厂模式通过组合的方式创建一系列的对象
用大白话再解释一波:
工厂方法模式侧重点在「方法」,定义一个抽象方法返回一个抽象类型「protected abstract Pizza createPizza(String type);
」,让子类去实现这个抽象方法,确定返回具体的对象类型,所以一个方法,只能创建一个对象
抽象工厂模式侧重点在「抽象工厂」,定义了一个工厂的接口,接口中有多个创建对象的方法,所以一个工厂,可以创建一系列的对象
抽象工厂的接口是由多个工厂方法组成!!