客戶(hù)需求
/**
* 小明在北京開(kāi)了一家pizza店,生意很好,此時(shí),小強(qiáng)和小紅都想加盟他的pizza店,
* 分別在廣東和湖南開(kāi)一家pizza店。(以后可能加盟店越來(lái)越多)
* 原料:dough, sauce, toppings,cheese(奶酪), clam(哈蜊),
* veggie(素食), pepperoni(意式香腸)(以后可能還有更多)
*
* 制作流程:準(zhǔn)備,烘烤,切割,打包
*
* 要求:1、廣東店和湖南店的口味不同,需適合當(dāng)?shù)厝说目谖? *
* 2、為保證披薩質(zhì)量,加盟店必須與北京店制作流程一致
*
* 3、必須防止加盟店使用低價(jià)原料來(lái)增加利潤(rùn)
*
* 請(qǐng)用代碼描述以上需求
*
*/
程序設(shè)計(jì)
1、PizzaStore是用來(lái)給客戶(hù)下訂單買(mǎi)pizza的,所以每個(gè)PizzaStore都會(huì)有一個(gè)orderPizza的方法,返回pizza給客戶(hù);
2、當(dāng)客戶(hù)下單后,就需要生產(chǎn)對(duì)應(yīng)的pizza,PizzaStore不需要知道如何去創(chuàng)造pizza,根據(jù)客戶(hù)的需求交給對(duì)應(yīng)的子類(lèi)去完成;
3、當(dāng)去生產(chǎn)滿(mǎn)足客戶(hù)需求的pizza時(shí),我們都會(huì)用new來(lái)獲取這個(gè)pizza的實(shí)例對(duì)象,此時(shí),我們需意識(shí)到,new pizza時(shí)是整個(gè)過(guò)程變化的部分,那么就需馬上想到我們之前學(xué)習(xí)策略模式時(shí)講過(guò)的設(shè)計(jì)原則:找出程序中可能需要變化之處,把它們獨(dú)立出來(lái),不要和那些不需要變化的代碼混在一起
4、廢話(huà)不多說(shuō),代碼實(shí)現(xiàn)
-
Pizza,若以后還需要添加更多的原料,直接增加屬性就可以了
public abstract class Pizza { /** * 披薩名稱(chēng) */ protected String mPizzaName; /** * 面粉類(lèi)型 */ protected String mPizzaDough; /** * 醬料類(lèi)型 */ protected String mPizzaSauce; /** * 其他佐料 */ protected ArrayList<String> mPizzaToppings = new ArrayList<>(); public void prepare() { System.out.println("準(zhǔn)備:" + mPizzaName); System.out.println("攪拌面粉:" + mPizzaDough); System.out.println("添加醬料:" + mPizzaSauce); for (int i = 0; i < mPizzaToppings.size(); i++) { System.out.println("其他佐料:" + mPizzaToppings.get(i)); } } /** * 不允許子類(lèi)修改烘烤時(shí)間 */ public final void bake() { System.out.println("大約烘烤25分鐘"); } public void cut() { System.out.println("將披薩切成小塊三角形狀"); } /** * 不允許子類(lèi)修改包裝方式 */ public final void box() { System.out.println("包裝好"); } public String getName() { return mPizzaName; } }
-
PizzaStore
public abstract class PizzaStore { /** * 根據(jù)客戶(hù)需求預(yù)訂披薩 * * @param type * 披薩類(lèi)型 * @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; // 這里可以用枚舉來(lái)表示pizza的原料內(nèi)容,防止客戶(hù)輸入錯(cuò)誤。這里就偷一下懶了 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"); } }
測(cè)試代碼
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());
}
}
測(cè)試結(jié)果
工廠方法模式
-
定義
定義了一個(gè)創(chuàng)建對(duì)象的抽象類(lèi),但由子類(lèi)決定要實(shí)例化的類(lèi)是哪一個(gè)。工廠方法讓類(lèi)把實(shí)例化推遲到子類(lèi)
Demo UML圖
接下來(lái),我們?yōu)槊總€(gè)區(qū)域創(chuàng)建原料工廠
public interface PizzaIngredientFactory
{
/*
* 創(chuàng)建原料的方法,為了方便,直接用字符串代替, 在實(shí)際項(xiàng)目中,原料可以用類(lèi)來(lái)表示
*/
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的代碼,添加兩種原料,并抽象準(zhǔn)備流程,讓子類(lèi)自己去準(zhǔn)備需要的原料
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("準(zhǔn)備:" + 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;
}
}
我們不需要設(shè)計(jì)不同的類(lèi)來(lái)處理不同口味的披薩,讓原料廠處理這種區(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店用到了湖南原料工廠,由該原料工廠負(fù)責(zé)生產(chǎn)所有湖南口味的披薩所需的原料
PizzaIngredientFactory ingredientFactory = new HuNanPizzaIngredientFactory();
if (type.equals("cheese"))
{
// 對(duì)象組合:把工廠傳遞給每一個(gè)披薩,以便披薩從工廠中取得原料
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;
}
}
測(cè)試代碼
PizzaStore NYStore = new NYStylePizzaStore();
Pizza pizzaTwo = NYStore.orderPizza("cheese");
System.out.println(pizzaTwo.getName());
此時(shí),你有沒(méi)有發(fā)現(xiàn)這個(gè)版本的createPizza()和之前的工廠方法實(shí)現(xiàn)的有什么不同?
我們引入了新類(lèi)型的工廠,也就是所謂的抽象工廠來(lái)創(chuàng)建披薩原料家族。通過(guò)抽象工廠所提供的接口,可以創(chuàng)建產(chǎn)品的家族,利用這個(gè)接口書(shū)寫(xiě)代碼,我們的代碼將從實(shí)際工廠解耦,以便在不同上下文中實(shí)現(xiàn)各式各樣的工廠,制造出各種不同的產(chǎn)品。
抽象工廠模式
-
定義
提供一個(gè)接口,用于創(chuàng)建相關(guān)或依賴(lài)對(duì)象的家族,而不需要明確指定具體類(lèi)
Demo UML類(lèi)圖
你可能注意到了,抽象工廠的每個(gè)方法實(shí)際上看起來(lái)都是工廠方法,因?yàn)槊總€(gè)方法都被聲明成抽象,而子類(lèi)的方法覆蓋這些法來(lái)創(chuàng)建某些對(duì)象。
那么,工廠方法是不是潛伏在抽象工廠里面了?
是的,抽象工廠的方法經(jīng)常以工廠方法的方式實(shí)現(xiàn)。抽象工廠的任務(wù)就是定義一個(gè)負(fù)責(zé)創(chuàng)建一組產(chǎn)品的接口,這個(gè)接口內(nèi)的每個(gè)方法都負(fù)責(zé)創(chuàng)建一個(gè)具體的產(chǎn)品,同時(shí)我們利用實(shí)現(xiàn)抽象工廠的子類(lèi)來(lái)提供這些具體的做法。所以,在抽象工廠中利用工廠方法實(shí)現(xiàn)生產(chǎn)方法是相當(dāng)自然的做法。
工廠方法模式與抽象工廠模式不同之處
- 工廠方法利用繼承的方式來(lái)創(chuàng)建對(duì)象,抽象工廠則是用的對(duì)象組合來(lái)創(chuàng)建對(duì)象
- 工廠方法只能生產(chǎn)同一等級(jí)結(jié)構(gòu)中的固定產(chǎn)品,而抽象工廠能夠生產(chǎn)不同產(chǎn)品族的全部產(chǎn)品
- 工廠方法的優(yōu)勢(shì):支持增加任意產(chǎn)品;抽象工廠的優(yōu)勢(shì):支持增加產(chǎn)品族,對(duì)于增加新的產(chǎn)品,需改變接口,可能會(huì)造成繁重的工作;
工廠模式中用到的設(shè)計(jì)原則
依賴(lài)倒置原則(Dependency Inversion Principle)
要依賴(lài)抽象,不要依賴(lài)具體類(lèi)
這個(gè)原則似乎聽(tīng)起來(lái)很像是“針對(duì)接口編程,不針對(duì)實(shí)現(xiàn)編程”,的確很相似,但這里更強(qiáng)調(diào)“抽象”。這個(gè)原則說(shuō)明了:不能讓高層組件依賴(lài)低層組件,并且,不管高層或低層組件,“兩者”都應(yīng)該依賴(lài)于抽象。
在我們的Demo中,PizzaStore是“高層組件”,而具體的pizza是”低層組件“,他們都依賴(lài)Pizza這個(gè)抽象類(lèi)。