披萨订购
假设要完成披萨订购的功能,披萨的种类很多,比如
GreekPizz
、CheesePizz
等,披萨店会根据用户需要的披萨种类制作披萨,制作的流程包括prepare->bake->cut->box
简单实现
下面代码的实现十分简单清晰,但是如果披萨类型增加或删除,需要去修改代码,不符合开闭原则(类应该对扩展开发,对修改关闭)
// 下面不同的pizza类型都继承了相同的父类
Pizza orderPizza(String type){
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("clam")){
pizza = new ClamPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
工厂模式
简单的披萨工厂-简单工厂模式
从上述代码可以看出,出了创建披萨对象的代码需要修改,其他代码都是不变的,所以可以将创建披萨对象的代码分离出来作为一个新对象-披萨工厂:
- 此时
orderPizza
方法变为该工厂的客户,它无需知道工厂如何得到一个披萨,只关心从工厂可以得到一个披萨,并且可以调用方法对披萨进行准备、烘烤等操作- 并且该工厂可以有许多客户,比如制作披萨点的菜单就可以利用该工厂制作的披萨来制定价钱等
// 简单披萨工厂
public class SimplePizzaFactory{
public Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("clam")){
pizza = new ClamPizza();
}
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;
}
加盟披萨店-工厂方法模式
此时在纽约等地区想在当地加盟该披萨店,并且希望工厂能加工出纽约风味的披萨
给每个地区创建一个工厂
下面代码的缺陷在于无法让各地区的加盟店决定自己的制作流程,只是决定采用哪个工厂而已
NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.orderPizza("Veggie"); // 在纽约工厂制作的纽约风味披萨
让加盟店自己决定制作流程
原本由一个对象负责所有具体类的实例化,现在通过子类负责实例化
public abstract class PizzaStore{
// orderPizza可以声明为final,防止被子类覆盖
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type) // 从createPizza方法从工厂中移到披萨店中
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 定义为抽象,必须实现
protected abstract Pizza createPizza(String type);
}
public class NYPizzaStrore extends PizzaStore{
// 子类全权负责实例化哪个具体Pizza,父类并不知道具体创建的披萨是哪一种,只知道这个披萨要进行烘烤、切片等流程
Pizza createPizza(String item){
if(item.equals("chess")){
return new NYStyleCheesePizza();
}else if(...){
...
}
}
}
// 定义一个抽象的披萨类
public abstract class Pizza{
String name;
void cut(){
System.out.println("切成方块");
}
}
// 纽约风味的披萨
public class NYStyleCheesePizza extends Pizza{
public NYStyleCheesePizza(){
name = "NYStyle with cheese"
}
void cut(){
System.out.println("切成三角形");
}
}
// 测试
public class Test{
public static void main(String[] agrs){
PizzaStore nyStore = new NYPizzaStrore();
Pizza pizza = nyStore.orderPizza("cheese"); // nyStore决定要制作什么披萨
}
}
工厂方法模式
定义了一个创建对象的接口,由子类决定要实例化哪个类
依赖倒置原则
对于工厂方法模式还依赖了一个原则-依赖倒置原则,该原则表示要依赖抽象,不要依赖具体类,换个说法就是高层组件和低层组件都应该依赖于抽象
使用当地原材料-抽象工厂模式
假设不同地区的加盟店制作披萨要求用当地的原材料制作
创建当前原料工厂
很明显可以想到,不可能只存在一家工厂生产不同地区需要的原料,然后空运过去,可以在当地创建一个原料工厂
// 抽象原料工厂
public interface PizzaIngredientFactory{
public Sauce createSauce();
public Cheese createCheese();
}
// 纽约的原料工厂
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
public Sauce createSauce(){
return new NYSauce(); // 使用当前材料
}
...
}
重新制作披萨
// 抽象披萨类,将prepare方法定义为抽象了
public abstract class Pizza{
String name;
Sauce sauce;
Cheese Cheese;
// 声明为抽象是因为现在当地披萨需要使用自己的原材料进行准备
abstract void prepare();
void cut(){
System.out.println("切成方块");
}
}
// 现在根据使用不同的原料工厂就可以区分出是哪个地区的CheesePizza了
public class CheesePizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
// 只需要传入需要地区的原料工厂即可
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory = ingredientFactory;
}
void prepare(){
// 通过传入的原料工厂就可以生产出该工程的sauce
sauce = ingredientFactory.createSauce();
...
}
}
public class NYPizzaStore extends PizzaStore{
Pizza createPizza(String item){
Pizza pizza = null;
// 纽约加盟店肯定使用纽约原料工厂了
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if(item.equals("chess")){
// 现在不需要写为NYStyleCheesePizza,只需要传入对于原料工厂即可
pizza = new CheesePizza(ingredientFactory);
}else if(...){
...
}
return pizza;
}
}
// 测试(代码没有变化)
public class Test{
public static void main(String[] agrs){
PizzaStore nyStore = new NYPizzaStrore();
Pizza pizza = nyStore.orderPizza("cheese");
}
}
抽象工厂模式
提供一个接口用于创建相关或依赖对象的家族,并且不需要明确具体类(比如在
NYPizzaStore
的createPizza
方法中,并没有明确具体的NYStyleCheesePizza
,而是传入一个原料工厂)