客戶需求
/**
* 小明在北京開了一家pizza店,生意很好奇钞,此時,小強和小紅都想加盟他的pizza店漂坏,
* 分別在廣東和湖南開一家pizza店景埃。(以后可能加盟店越來越多)
* 原料:dough, sauce, toppings,cheese(奶酪), clam(哈蜊),
* veggie(素食), pepperoni(意式香腸)(以后可能還有更多)
*
* 制作流程:準備,烘烤顶别,切割谷徙,打包
*
* 要求:1、廣東店和湖南店的口味不同驯绎,需適合當?shù)厝说目谖? *
* 2完慧、為保證披薩質量,加盟店必須與北京店制作流程一致
*
* 3剩失、必須防止加盟店使用低價原料來增加利潤
*
* 請用代碼描述以上需求
*
*/
程序設計
1屈尼、PizzaStore是用來給客戶下訂單買pizza的,所以每個PizzaStore都會有一個orderPizza的方法拴孤,返回pizza給客戶脾歧;
2、當客戶下單后演熟,就需要生產對應的pizza鞭执,PizzaStore不需要知道如何去創(chuàng)造pizza,根據(jù)客戶的需求交給對應的子類去完成绽媒;
3蚕冬、當去生產滿足客戶需求的pizza時,我們都會用new來獲取這個pizza的實例對象是辕,此時,我們需意識到猎提,new pizza時是整個過程變化的部分获三,那么就需馬上想到我們之前學習策略模式時講過的設計原則:找出程序中可能需要變化之處旁蔼,把它們獨立出來,不要和那些不需要變化的代碼混在一起
4疙教、廢話不多說棺聊,代碼實現(xiàn)
-
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 { /** * 根據(jù)客戶需求預訂披薩 * * @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());
}
}
測試結果
工廠方法模式
-
定義
定義了一個創(chuàng)建對象的抽象類裸弦,但由子類決定要實例化的類是哪一個祟同。工廠方法讓類把實例化推遲到子類
Demo UML圖
接下來,我們?yōu)槊總€區(qū)域創(chuàng)建原料工廠
public interface PizzaIngredientFactory
{
/*
* 創(chuàng)建原料的方法理疙,為了方便晕城,直接用字符串代替, 在實際項目中窖贤,原料可以用類來表示
*/
public String createDough();
public String createSauce();
public String createCheese();
public String[] createVeggies();
public String createPepperoni();
public String createClams();
}
不同區(qū)域有不同的原料工廠
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;
}
}
我們不需要設計不同的類來處理不同口味的披薩滤蝠,讓原料廠處理這種區(qū)域差異就可以了。
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());
此時,你有沒有發(fā)現(xiàn)這個版本的createPizza()和之前的工廠方法實現(xiàn)的有什么不同粤攒?
我們引入了新類型的工廠所森,也就是所謂的抽象工廠來創(chuàng)建披薩原料家族。通過抽象工廠所提供的接口夯接,可以創(chuàng)建產品的家族焕济,利用這個接口書寫代碼,我們的代碼將從實際工廠解耦盔几,以便在不同上下文中實現(xiàn)各式各樣的工廠晴弃,制造出各種不同的產品。
抽象工廠模式
-
定義
提供一個接口逊拍,用于創(chuàng)建相關或依賴對象的家族上鞠,而不需要明確指定具體類
Demo UML類圖
你可能注意到了,抽象工廠的每個方法實際上看起來都是工廠方法芯丧,因為每個方法都被聲明成抽象芍阎,而子類的方法覆蓋這些法來創(chuàng)建某些對象。
那么缨恒,工廠方法是不是潛伏在抽象工廠里面了谴咸?
是的轮听,抽象工廠的方法經(jīng)常以工廠方法的方式實現(xiàn)。抽象工廠的任務就是定義一個負責創(chuàng)建一組產品的接口岭佳,這個接口內的每個方法都負責創(chuàng)建一個具體的產品血巍,同時我們利用實現(xiàn)抽象工廠的子類來提供這些具體的做法。所以珊随,在抽象工廠中利用工廠方法實現(xiàn)生產方法是相當自然的做法述寡。
工廠方法模式與抽象工廠模式不同之處
- 工廠方法利用繼承的方式來創(chuàng)建對象,抽象工廠則是用的對象組合來創(chuàng)建對象
- 工廠方法只能生產同一等級結構中的固定產品叶洞,而抽象工廠能夠生產不同產品族的全部產品
- 工廠方法的優(yōu)勢:支持增加任意產品鲫凶;抽象工廠的優(yōu)勢:支持增加產品族,對于增加新的產品京办,需改變接口掀序,可能會造成繁重的工作;
工廠模式中用到的設計原則
依賴倒置原則(Dependency Inversion Principle)
要依賴抽象惭婿,不要依賴具體類
這個原則似乎聽起來很像是“針對接口編程不恭,不針對實現(xiàn)編程”,的確很相似财饥,但這里更強調“抽象”换吧。這個原則說明了:不能讓高層組件依賴低層組件,并且钥星,不管高層或低層組件沾瓦,“兩者”都應該依賴于抽象。
在我們的Demo中谦炒,PizzaStore是“高層組件”贯莺,而具體的pizza是”低層組件“,他們都依賴Pizza這個抽象類宁改。