亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

《Head first設計模式》學習筆記 – 工廠方法模式

2019-11-08 00:24:38
字體:
來源:轉載
供稿:網友

工廠方法模式定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到了子類。

預定披薩

假設你有一個披薩店,預定披薩的代碼可能是這么寫的:

Pizza orderPizza(){    Pizza pizza = new Pizza();     // 準備面皮,加調料等    pizza.PRepare();    // 烘烤    pizza.bake();    // 切片    pizza.cut();    // 裝盒    pizza.box();     return pizza;}

更多的披薩類型

但是,你現在需要更多的披薩類型。你必須增加一些代碼,來“決定”適合的披薩類型,然后再“制造”這個披薩:

/ 現在把披薩類型傳入orderPizzaPizza orderPizza(String type) {    Pizza pizza;     // 根據披薩的類型,我們實例化正確的具體類,然后將其賦值給pizza實例變量。    // 請注意,這里的任何披薩都必須實現pizza接口。    if (type.equals("cheese")) {        pizza = new CheesePizza();    } else if (type.equals("greek")) {        pizza = new GreekPizza();    } else if (type.equals("pepperoni")) {        pizza = new Pepperonipizza();    }     pizza.prepare();    pizza.bake();    pizza.cut();    pizza.box();     return pizza;}

修改披薩類型

你發現你所有的競爭者都已經在他們的菜單中加入了一些流行風味的披薩:Clam Pizza(蛤蜊披薩)、Veggie Pizza(素食披薩)。很明顯,你必須要趕上他們,所以也要把這些風味加進你的菜單中。而最近Greek Pizza(希臘披薩)賣得不好,所以你決定將它從菜單中去掉:

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 PepperoniPizza();    } else if (type.equals("clam")) {        pizza = new ClamPizza();    } else if (type.equals("veggie")) {        pizza = new VeggiePizza();    }     // 這里是我們不想改變的地方。因為披薩的準備、烘烤、包裝,多年來持續不變,    // 所以這部分的代碼不會改變,只有發生這些動作的披薩會改變。    pizza.prepare();    pizza.bake();    pizza.cut();    pizza.box();     return pizza;}很明顯地,如果實例化“某些”具體類,將使orderPizza()出問題,而且也無法讓orderPizza()對修改關閉。但是,現在我們已經知道哪些會改變,哪些不會改變,該是使用封裝的時候了。

建立一個簡單披薩工廠

現在最好將創建對象移到orderPizza()之外,但怎么做呢?我們可以把創建披薩的代碼移到另一個對象中,由這個新對象專職創建披薩。我們稱這個新對象為“工廠”。工廠(factory)處理創建對象的細節。一旦有了SimplePizzaFactory,orderPizza()就變成此對象的客戶。當需要披薩時,就叫披薩工廠做一個。那些orderPizza()方法需要知道希臘披薩或者蛤蜊披薩的日子一去不復返了?,F在orderPizza()方法只關心從工廠得到了一個披薩,而這個披薩實現了Pizza接口,所以它可以調用prepare()、bake()、cut()、box()來分別進行準備、烘烤、切片、裝盒。

/ SimplePizzaFactory是我們的新類,它只做一件事情:幫它的客戶創建披薩public class SimplePizzaFactory {    // 首先,在這個工廠內定義一個createPizza()方法,所有客戶用這個方法來實例化新對象。    public Pizza createPizza(String type) {        Pizza pizza = null;         // 這是從orderPizza()方法中移過來的代碼        if (type.equals("cheese")) {            pizza = new CheesePizza();        } else if (type.equals("pepperoni")) {            pizza = new PepperoniPizza();        } else if (type.equals("clam")) {            pizza = new ClamPizza();        } else if (type.equals("veggie")) {            pizza = new VeggiePizza();        }        return pizza;    }} // 現在我們為PizzaStore加上一個對SimplePizzaFactory的引用public class PizzaStore {    SimplePizzaFactory factory;     // PizzaStore的構造器,需要一個工廠作為參數    public PizzaStore(SimplePizzaFactory factory){        this.factory = factory;    }     public Pizza orderPizza(String type) {        Pizza pizza;         // orderPizza()方法通過簡單傳入訂單類型來使用工廠創建披薩。        // 請注意,我們把new操作符替換成工廠對象的創建方法。這里不再使用具體實例化        pizza = factory.createPizza(type);         pizza.prepare();        pizza.bake();        pizza.cut();        pizza.box();         return pizza;    }}

加盟披薩店

你的披薩店經營有成,擊敗了競爭者,現在大家都希望能在自家附近有加盟店。身為加盟公司經營者,你希望確保加盟店運營的質量,所以希望這些店都使用你那些經過時間考驗的代碼。但是區域的差異呢?每家加盟店都可能想要提供不同風味的披薩(比方說紐約、芝加哥、加州),這受到了開店地點及該地區披薩美食家口味的影響。在推廣SimplePizzaFactory時,你發現加盟店的確是采用你的工廠創建披薩,但是其他部分,卻開始采用他們自創的流程:烘烤的做法有差異、不要切片、使用其他廠商的盒子。能不能建立一個框架,把加盟店和創建披薩捆綁在一起的同時又保持一定的彈性?

給披薩店使用的框架

有個做法可讓披薩制作活動局限于PizzaStore類,而同時又能讓這些加盟店依然可以自由地制作該區域的風味。所要做的事情,就是把createPizza()方法放回到PizzaStore中,不過要把它設置成“抽象方法”,然后為每個區域風味創建一個PizzaStore的子類。首先,看PizzaStore所做的改變:

public abstract class PizzaStore {     public Pizza orderPizza(String type) {        Pizza pizza;         // 現在createPizza()方法從工廠對象中移回PizzaStore        pizza = createPizza(type);         // 這些都沒變        pizza.prepare();        pizza.bake();        pizza.cut();        pizza.box();         return pizza;    }     // 現在把工廠對象移到這個方法中    // 在PizzaStore里,“工廠方法”現在是抽象的    abstract Pizza createPizza(String type);}現在已經有一個PizzaStore作為超類;讓每個域類型(NYPizzaStore、ChicagoPizzaStore、CaliforniaPizzaStore)都繼承這個PizzaStore,每個子類各自決定如何制作披薩。

允許子類做決定

PizzaStore已經有一個不錯的訂單系統,由orderPizza()方法負責處理訂單,而你希望所有加盟店對于訂單的處理都能一致。各個區域披薩店之間的差異在于他們制作披薩的風味(紐約披薩的薄脆、芝加哥披薩的餅厚等),我們現在要讓現在createPizza()能夠應對這些變化來負責創建正確種類的披薩。做法是讓PizzaStore的各個子類負責定義自己的createPizza()方法。所以我們會得到一些PizzaStore具體的子類,每個子類都有自己的披薩變體,而仍然適合PizzaStore框架,并使用調試好的orderPizza()方法

public abstract class PizzaStore {     public Pizza orderPizza(String type) {        Pizza pizza;         pizza = createPizza(type);         pizza.prepare();        pizza.bake();        pizza.cut();        pizza.box();         return pizza;    }     // 每個子類都會覆蓋createPizza()方法    abstract Pizza createPizza(String type);} // 如果加盟店為顧客提供紐約風味的披薩,就使用NyStylePizzaStore,// 因為此類的createPizza()方法會建立紐約風味的披薩public class NyStylePizzaStore extends PizzaStore{     @Override    Pizza createPizza(String type) {        Pizza pizza = null;         if (type.equals("cheese")) {            pizza = new NyStyleCheesePizza();        } else if (type.equals("pepperoni")) {            pizza = new NyStylePepperoniPizza();        } else if (type.equals("clam")) {            pizza = new NyStyleClamPizza();        } else if (type.equals("veggie")) {            pizza = new NyStyleVeggiePizza();        }        return pizza;    }} // 類似的,利用芝加哥子類,我們得到了帶芝加哥原料的createPizza()實現public class ChicagoStylePizzaStore extends PizzaStore{     @Override    Pizza createPizza(String type) {        Pizza pizza = null;         if (type.equals("cheese")) {            pizza = new ChicagoCheesePizza();        } else if (type.equals("pepperoni")) {            pizza = new ChicagoPepperoniPizza();        } else if (type.equals("clam")) {            pizza = new ChicagoClamPizza();        } else if (type.equals("veggie")) {            pizza = new ChicagoVeggiePizza();        }        return pizza;    }}現在問題來了,PizzaStore的子類終究只是子類,如何能夠做決定?在NyStylePizzaStore類中,并沒有看到任何做決定邏輯的代碼。關于這個方面,要從PizzaStore的orderPizza()方法觀點來看,此方法在抽象的PizzaStore內定義,但是只在子類中實現具體類型。orderPizza()方法對對象做了許多事情(例如:準備、烘烤、切片、裝盒),但由于Pizza對象是抽象的,orderPizza()并不知道哪些實際的具體類參與進來了。換句話說,這就是解耦(decouple)!當orderPizza()調用createPizza()時,某個披薩店子類將負責創建披薩。做哪一種披薩呢?當然是由具體的披薩店決定。那么,子類是實時做出這樣的決定嗎?不是,但從orderPizza()的角度看,如果選擇在NyStylePizzaStore訂購披薩,就是由這個子類(NyStylePizzaStore)決定。嚴格來說,并非由這個子類實際做“決定”,而是由“顧客”決定哪一家風味的披薩店才決定了披薩的風味。

工廠方法模式

工廠方法模式定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到了子類。

工廠方法模式(Factory Method Pattern)通過讓子類決定該創建的對象是什么,來達到將對象創建的過程封裝的目的。PizzaStore就是創建者(Creator)類。它定義了一個抽象的工廠方法,讓子類實現此方法制造產品。創建者通常會包含依賴于抽象產品的代碼,而這些抽象產品由子類制造。創建者不需要真的知道在制造哪種具體產品。能夠產生產品的類稱為具體創建者。NYPizzaStore和ChicagoPizzaStore就是具體創建者。Pizza是產品類。工廠生產產品,對PizzaStore來說,產品就是Pizza。抽象的Creator提供了一個創建對象的方法的接口,也稱為“工廠方法”。在抽象的Creator中,任何其他實現的方法,都可能使用到這個工廠方法所制造出來的產品,但只有子類真正實現這個工廠方法并創建產品。

// Creator是一個類,它實現了所有操縱產品的方法,但不實現工廠方法public abstract class Creator{    void anOperation(){        // ...    }    // Creator的所有子類都必須實現這個抽象的factoryMethod()方法    abstract void factoryMethod();} // 具體的創建者public class ConcreteCreator extends Creator{    // ConcreteCreator實現了factoryMethod(),以實際制造出產品。    @Override    void factoryMethod() {        // ...    }} // 所有產品必須實現這個接口,這樣一來,// 使用這些產品的類就可以引用這個接口,而不是具體的類public abstract class Product{    void operation(){        // ...    }} // 具體的產品public class ConcreteProduct extends Product{}

依賴倒置原則

假設你從未聽說過OO工廠。下面是一個不使用工廠模式的披薩店版本。數一數,這個類所依賴的具體披薩對象有幾種。

public class DependentPizzaStore {     public Pizza createPizza(String style, String type) {        Pizza pizza = null;         if(style.equals("NY")){            // 處理所有紐約風味的披薩            if (type.equals("cheese")) {                pizza = new NyStyleCheesePizza();            } else if (type.equals("pepperoni")) {                pizza = new NyStylePepperoniPizza();            } else if (type.equals("clam")) {                pizza = new NyStyleClamPizza();            } else if (type.equals("veggie")) {                pizza = new NyStyleVeggiePizza();            }        } else if(style.equals("Chicago")){            // 處理所有芝加哥風味的披薩            if (type.equals("cheese")) {                pizza = new ChicagoCheesePizza();            } else if (type.equals("pepperoni")) {                pizza = new ChicagoPepperoniPizza();            } else if (type.equals("clam")) {                pizza = new ChicagoClamPizza();            } else if (type.equals("veggie")) {                pizza = new ChicagoVeggiePizza();            }        } else {            System.out.println("Error");            return null;        }         pizza.prepare();        pizza.bake();        pizza.cut();        pizza.box();         return pizza;    }}如果把這個版本的披薩店和它依賴的對象畫成一張圖,看起來是這樣的:

這個版本的PizzaStore依賴于所有的披薩對象,因為它直接創建這些披薩對象。如果這些類的實現改變了,那么可能必須修改PizzaStore。每新增一個披薩類型,就等于讓PizzaStore多了一個依賴。因為對于披薩具體實現的任何改變都會影響到PizzaStore,我們說PizzaStore“依賴于”披薩的實現。很清楚的,代碼里減少對于具體類的依賴是件好事。有一個OO設計原則就正式闡明了這一點:

依賴倒置原則:要依賴抽象,不要依賴具體類。

這個原則說明了:不能讓高層組件依賴低層組件,而且,不管高層或低層組件,兩者都應該依賴于抽象。比如,這個例子里的PizzaStore是高層組件,而披薩實現是低層組件,很清楚的,PizzaStore依賴這些具體披薩類。現在,這個原則告訴我們,應該重寫代碼以便于我們依賴抽象類,而不依賴具體類。對于高層及低層模塊都應該如此。

依賴倒置原則的應用

非常依賴披薩店的主要問題在于:它依賴每個披薩類型。因為它是在自己的orderPizza()方法中,實例化這些具體類型的。如何在orderPizza()方法中,將這些實例化對象的代碼獨立出來?我們知道,工廠方法剛好能派上用場。所以,應用工廠方法后,類圖看起來就像這樣:

factory_pattern_dependency_2

PizzaStore現在依賴Pizza這個抽象類。具體披薩類也依賴Pizza抽象,因為它們實現了Pizza接口。在應用工廠方法后,高層組件(也就是PizzaStore)和低層組件(也就是這些披薩)都依賴了Pizza抽象。想要遵循依賴倒置原則,工廠方法并非是唯一的技巧,但卻是最有威力的技巧之一。

遵循依賴倒置原則的指導方針

下面的指導方針,能幫你避免在OO設計中違反依賴倒置原則:

變量不可以持有具體類的引用

如果使用new,就會持有具體類的引用。你可以改用工廠來避開這樣的做法。

不要讓類派生自具體類

如果派生自具體類,你就會依賴具體類。請派生自一個抽象(接口或抽象類)。

不要覆蓋基類中已實現的方法

如果覆蓋基類已實現的方法,那么你的基類就不是一個真正適合被繼承的抽象?;愔幸褜崿F的方法,應該由所有的子類共享。要完全遵守這些指導方針似乎不太可能,但是如果你深入體驗這些方針,將這些方針內化成你思考的一部分,那么在設計時,你將知道何時有足夠的理由違反這樣的原則。比方說,如果有一個不像是會改變的類,那么在代碼中直接實例化具體類也就沒什么大礙。另一方面,如果有個類可能改變,你可以采用一些好技巧(例如工廠方法)來封裝改變。

原文出處: cashow


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品国产91久久久久久老师| 亚洲成av人乱码色午夜| 国产精品久久久久久超碰| 中文字幕亚洲自拍| 久久精品国产成人精品| 国产中文字幕日韩| 亚洲图片在区色| 亚洲天堂免费观看| 日韩在线观看精品| 国产日韩精品入口| 国产在线高清精品| 亚洲精品www久久久久久广东| 成人精品久久一区二区三区| 久久久国产精彩视频美女艺术照福利| 日韩欧美视频一区二区三区| 欧美激情va永久在线播放| 伊人亚洲福利一区二区三区| 国产99久久精品一区二区永久免费| 欧美专区福利在线| 宅男66日本亚洲欧美视频| 日本国产高清不卡| 国产高清在线不卡| 国产精品网站大全| 久久艳片www.17c.com| 欧美精品在线播放| 亚洲国产欧美久久| 欧美午夜精品久久久久久人妖| 欧美香蕉大胸在线视频观看| 日韩精品极品毛片系列视频| 国产欧美日韩精品在线观看| 伊人精品在线观看| 国产精品视频一区国模私拍| 亚洲欧美www| 欧美成人精品h版在线观看| 色妞在线综合亚洲欧美| 欧美精品在线播放| 久久久成人精品| 日韩国产精品一区| 国产一区二区三区在线观看视频| 日韩黄色在线免费观看| 一个人www欧美| 亚洲国产精品视频在线观看| 国产精品免费看久久久香蕉| 欧美成人精品一区二区| 欧美在线xxx| 亲爱的老师9免费观看全集电视剧| 91美女片黄在线观| 不卡中文字幕av| 日韩免费中文字幕| 韩国v欧美v日本v亚洲| 国产精品丝袜一区二区三区| 26uuu国产精品视频| 久久视频在线播放| 91香蕉电影院| 激情成人在线视频| 国产+人+亚洲| 亚洲另类图片色| 国产精品亚洲网站| 国内精品在线一区| 国产精品久久色| 91精品国产自产在线观看永久| 亚洲精品按摩视频| 国产精品免费在线免费| 欧美视频在线视频| 国产精品久久在线观看| 成人做爰www免费看视频网站| 午夜精品国产精品大乳美女| 国产精品久久av| 日韩av网站电影| 欧美亚洲伦理www| 国产精品视频永久免费播放| 国产情人节一区| 日韩欧美福利视频| 欧美又大粗又爽又黄大片视频| 亚洲国产精品va在线观看黑人| 欧美日韩美女视频| 久久久久久这里只有精品| 日韩日本欧美亚洲| 国产aⅴ夜夜欢一区二区三区| 亚洲精品中文字幕女同| 91影院在线免费观看视频| 日韩三级成人av网| 亚洲国产精品yw在线观看| 久久躁狠狠躁夜夜爽| 国产精品中文久久久久久久| 日本久久久久亚洲中字幕| 亚洲精品456在线播放狼人| 国产精品久久久久久久久久东京| 国产一级揄自揄精品视频| 欧美亚洲国产视频小说| 亚洲免费精彩视频| 青青草99啪国产免费| 91夜夜未满十八勿入爽爽影院| 欧美性猛交99久久久久99按摩| 国产一区二区视频在线观看| 欧美成人三级视频网站| 91在线高清免费观看| 国产精品精品一区二区三区午夜版| 日韩中文字幕在线精品| 国产成一区二区| 久久成人人人人精品欧| 日韩精品免费综合视频在线播放| 26uuu亚洲伊人春色| 性欧美视频videos6一9| 亚洲嫩模很污视频| 欧美韩日一区二区| 亚洲人成在线免费观看| 国产第一区电影| 亚洲国产中文字幕在线观看| 91香蕉国产在线观看| 久久不射热爱视频精品| 日本成人激情视频| 日韩av一区在线观看| 日本电影亚洲天堂| 色综合视频一区中文字幕| 成人性生交大片免费看小说| 日韩激情av在线播放| 亚洲综合日韩中文字幕v在线| 精品少妇一区二区30p| 96精品视频在线| 国产精品一区二区电影| 亚洲欧洲第一视频| 中文字幕欧美日韩精品| 亚洲免费中文字幕| 超碰97人人做人人爱少妇| 久久亚洲精品小早川怜子66| 成人国产精品日本在线| 国产999精品| 日韩精品免费在线观看| 国产欧美日韩精品在线观看| 国产精品久久久久久久久久久久久| 中文字幕一区日韩电影| 久久九九热免费视频| 亚洲国产欧美一区二区三区同亚洲| 国产欧美一区二区三区在线看| 欧美一级高清免费| 人人澡人人澡人人看欧美| 欧美大秀在线观看| 日韩中文av在线| 亚洲影院色在线观看免费| 一区二区三区国产视频| 欧美极品少妇xxxxⅹ裸体艺术| 91国产中文字幕| 狠狠躁18三区二区一区| 国产精品jvid在线观看蜜臀| 亚洲精品99久久久久中文字幕| 欧日韩在线观看| 日韩精品免费在线视频观看| 91久久在线播放| 亚洲美女在线看| 国产精品久久久久9999| 国产美女直播视频一区| 亚洲第一视频网站| 狠狠色香婷婷久久亚洲精品| 欧美日韩在线一区| 久久久久中文字幕| 97碰在线观看| 97视频免费在线看| 日本成人在线视频网址| 欧美伊久线香蕉线新在线| 国内外成人免费激情在线视频| 亚洲自拍偷拍福利| 日韩欧美一区二区三区久久| 欧美日韩国产中文精品字幕自在自线|