客户需求
/**
* 小明在北京开了一家pizza店,生意很好,此时,小强和小红都想加盟他的pizza店,
* 分别在广东和湖南开一家pizza店。(以后可能加盟店越来越多)
* 原料:dough, sauce, toppings,cheese(奶酪), clam(哈蜊),
* veggie(素食), pepperoni(意式香肠)(以后可能还有更多)
*
* 制作流程:准备,烘烤,切割,打包
*
* 要求:1、广东店和湖南店的口味不同,需适合当地人的口味
*
* 2、为保证披萨质量,加盟店必须与北京店制作流程一致
*
* 3、必须防止加盟店使用低价原料来增加利润
*
* 请用代码描述以上需求
*
*/
程序设计
1、PizzaStore是用来给客户下订单买pizza的,所以每个PizzaStore都会有一个orderPizza的方法,返回pizza给客户;
2、当客户下单后,就需要生产对应的pizza,PizzaStore不需要知道如何去创造pizza,根据客户的需求交给对应的子类去完成;
3、当去生产满足客户需求的pizza时,我们都会用new来获取这个pizza的实例对象,此时,我们需意识到,new pizza时是整个过程变化的部分,那么就需马上想到我们之前学习策略模式时讲过的设计原则:找出程序中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
4、废话不多说,代码实现
-
Pizza,若以后还需要添加更多的原料,直接增加属性就可以了
public abstract class Pizza { /** * 披萨名称 */ protected String mPizzaName; /** * 面粉类型 */ protected String mPizzaDough; /** * 酱料类型 */ protected String mPizzaSauce; /** * 其他佐料 */ protected ArrayList<String> mPizzaToppings = new ArrayList<>(); public void prepare() { System.out.println("准备:" + mPizzaName); System.out.println("搅拌面粉:" + mPizzaDough); System.out.println("添加酱料:" + mPizzaSauce); for (int i = 0; i < mPizzaToppings.size(); i++) { System.out.println("其他佐料:" + mPizzaToppings.get(i)); } } /** * 不允许子类修改烘烤时间 */ public final void bake() { System.out.println("大约烘烤25分钟"); } public void cut() { System.out.println("将披萨切成小块三角形状"); } /** * 不允许子类修改包装方式 */ public final void box() { System.out.println("包装好"); } public String getName() { return mPizzaName; } }
-
PizzaStore
public abstract class PizzaStore { /** * 根据客户需求预订披萨 * * @param type * 披萨类型 * @return */ public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } public abstract Pizza createPizza(String type); }
-
GuangDongStylePizzaStore
public class GuangDongStylePizzaStore extends PizzaStore { @Override public Pizza createPizza(String type) { Pizza pizza = null; // 这里可以用枚举来表示pizza的原料内容,防止客户输入错误。这里就偷一下懒了 if (type.equals("cheese")) { pizza = new GuangDongStyleCheesePizza(); } else if (type.equals("pepperoni")) { pizza = new GuangDongStylePepperoniPizza(); } else if (type.equals("clam")) { pizza = new GuangDongStyleClamPizza(); } else if (type.equals("veggie")) { pizza = new GuangDongStyleVeggiePizza(); } return pizza; } }
-
HuNanStylePizzaStore
public class HuNanStylePizzaStore extends PizzaStore { @Override public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("cheese")) { pizza = new HuNanStyleCheesePizza(); } else if (type.equals("pepperoni")) { pizza = new HuNanStylePepperoniPizza(); } else if (type.equals("clam")) { pizza = new HuNanStyleClamPizza(); } else if (type.equals("veggie")) { pizza = new HuNanStyleVeggiePizza(); } return pizza; } }
-
这里只贴出两种CheesePizza的代码
/** * 湖南口味奶酪披萨 * * */ public class HuNanStyleCheesePizza extends Pizza { public HuNanStyleCheesePizza() { mPizzaName = "HuNan Style Deep Dish Cheese Pizza"; mPizzaDough = "Extra Thick Crust Dough"; mPizzaSauce = "Plum Tomato Sauce"; mPizzaToppings.add("Shredded Mozzarella Cheese"); } @Override public void cut() { System.out.println("将披萨切成小块矩形状"); } } ----------------------------------------------------------- /** * 广东口味奶酪披萨 * * */ public class GuangDongStyleCheesePizza extends Pizza { public GuangDongStyleCheesePizza() { mPizzaName = "New York Style Sauce and Cheese Pizza"; mPizzaDough = "Thin Crust Dough"; mPizzaSauce = "Marinara Sauce"; mPizzaToppings.add("Grated Reggiano Cheese"); } }
测试代码
public class FactoryDesignPatternTest
{
public static void main(String[] args)
{
PizzaStore huNanPizzaStore = new HuNanStylePizzaStore();
PizzaStore guangDongPizzaStore = new GuangDongStylePizzaStore();
Pizza huNanPizza = huNanPizzaStore.orderPizza("cheese");
System.out.println(huNanPizza.getName());
System.out.println("-----------------------------------");
Pizza guangDongPizza = guangDongPizzaStore.orderPizza("cheese");
System.out.println(guangDongPizza.getName());
}
}
测试结果
工厂方法模式
-
定义
定义了一个创建对象的抽象类,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
Demo UML图
接下来,我们为每个区域创建原料工厂
public interface PizzaIngredientFactory
{
/*
* 创建原料的方法,为了方便,直接用字符串代替, 在实际项目中,原料可以用类来表示
*/
public String createDough();
public String createSauce();
public String createCheese();
public String[] createVeggies();
public String createPepperoni();
public String createClams();
}
不同区域有不同的原料工厂
public class HuNanPizzaIngredientFactory implements PizzaIngredientFactory
{
@Override
public String createDough()
{
return "ThinCrustDough";
}
@Override
public String createSauce()
{
return "MarinaraSauce";
}
@Override
public String createCheese()
{
return "ReggianoCheese";
}
@Override
public String[] createVeggies()
{
return new String[] { "Garlic", "Onion", "Mushroom", "RedPepper" };
}
@Override
public String createPepperoni()
{
return "SlicedPepperoni";
}
@Override
public String createClams()
{
return "FreshClams";
}
}
----------------------------------------------------------------------
public class GuangDongPizzaIngredientFactory implements PizzaIngredientFactory
{
@Override
public String createDough()
{
return "ThickCrustDough";
}
@Override
public String createSauce()
{
return "PlumTomatoSauce";
}
@Override
public String createCheese()
{
return "MozzarellaCheese";
}
@Override
public String[] createVeggies()
{
return new String[] { "BlackOlives", "Spinach", "Eggplant" };
}
@Override
public String createPepperoni()
{
return "SlicedPepperoni";
}
@Override
public String createClams()
{
return "FrozenClams";
}
}
重新修改一下pizza的代码,添加两种原料,并抽象准备流程,让子类自己去准备需要的原料
public abstract class Pizza
{
protected String mPizzaName;
protected String mPizzaDough;
protected String mPizzaSauce;
protected ArrayList<String> mPizzaToppings = new ArrayList<>();
//新添加的原料
protected String mPizzaCheese;
protected String mPizzaClam;
// public void prepare()
// {
// System.out.println("准备:" + mPizzaName);
// System.out.println("搅拌面粉:" + mPizzaDough);
// System.out.println("添加酱料:" + mPizzaSauce);
// for (int i = 0; i < mPizzaToppings.size(); i++)
// {
// System.out.println("其他佐料:" + mPizzaToppings.get(i));
// }
//}
protected abstract void prepareIngredient();
public final void bake()
{
System.out.println("大约烘烤25分钟");
}
public void cut()
{
System.out.println("将披萨切成小块三角形状");
}
public final void box()
{
System.out.println("包装好");
}
public void setName(String name)
{
mPizzaName = name;
}
public String getName()
{
return mPizzaName;
}
}
我们不需要设计不同的类来处理不同口味的披萨,让原料厂处理这种区域差异就可以了。
public class CheesePizza extends Pizza
{
private PizzaIngredientFactory mIngredientFactory;
public CheesePizza(PizzaIngredientFactory factory) {
mIngredientFactory = factory;
}
@Override
protected void prepareIngredient()
{
System.out.println("preparing:" + mPizzaName);
mPizzaDough = mIngredientFactory.createDough();
mPizzaSauce = mIngredientFactory.createSauce();
mPizzaCheese = mIngredientFactory.createCheese();
}
}
再回我到我们的Pizza店
public class HuNanStylePizzaStore extends PizzaStore
{
@Override
public Pizza createPizza(String type)
{
Pizza pizza = null;
// 湖南Pizza店用到了湖南原料工厂,由该原料工厂负责生产所有湖南口味的披萨所需的原料
PizzaIngredientFactory ingredientFactory = new HuNanPizzaIngredientFactory();
if (type.equals("cheese"))
{
// 对象组合:把工厂传递给每一个披萨,以便披萨从工厂中取得原料
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
}
else if (type.equals("pepperoni"))
{
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");
}
else if (type.equals("clam"))
{
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
}
else if (type.equals("veggie"))
{
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("New York Style Veggie Pizza");
}
return pizza;
}
}
测试代码
PizzaStore NYStore = new NYStylePizzaStore();
Pizza pizzaTwo = NYStore.orderPizza("cheese");
System.out.println(pizzaTwo.getName());
此时,你有没有发现这个版本的createPizza()和之前的工厂方法实现的有什么不同?
我们引入了新类型的工厂,也就是所谓的抽象工厂来创建披萨原料家族。通过抽象工厂所提供的接口,可以创建产品的家族,利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。
抽象工厂模式
-
定义
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
Demo UML类图
你可能注意到了,抽象工厂的每个方法实际上看起来都是工厂方法,因为每个方法都被声明成抽象,而子类的方法覆盖这些法来创建某些对象。
那么,工厂方法是不是潜伏在抽象工厂里面了?
是的,抽象工厂的方法经常以工厂方法的方式实现。抽象工厂的任务就是定义一个负责创建一组产品的接口,这个接口内的每个方法都负责创建一个具体的产品,同时我们利用实现抽象工厂的子类来提供这些具体的做法。所以,在抽象工厂中利用工厂方法实现生产方法是相当自然的做法。
工厂方法模式与抽象工厂模式不同之处
- 工厂方法利用继承的方式来创建对象,抽象工厂则是用的对象组合来创建对象
- 工厂方法只能生产同一等级结构中的固定产品,而抽象工厂能够生产不同产品族的全部产品
- 工厂方法的优势:支持增加任意产品;抽象工厂的优势:支持增加产品族,对于增加新的产品,需改变接口,可能会造成繁重的工作;
工厂模式中用到的设计原则
依赖倒置原则(Dependency Inversion Principle)
要依赖抽象,不要依赖具体类
这个原则似乎听起来很像是“针对接口编程,不针对实现编程”,的确很相似,但这里更强调“抽象”。这个原则说明了:不能让高层组件依赖低层组件,并且,不管高层或低层组件,“两者”都应该依赖于抽象。
在我们的Demo中,PizzaStore是“高层组件”,而具体的pizza是”低层组件“,他们都依赖Pizza这个抽象类。