headfirst設計模式(5)—工廠模式體系分析及抽象工廠模式

来源:https://www.cnblogs.com/skyseavae/archive/2018/10/29/9261119.html
-Advertisement-
Play Games

先編一個這麼久不寫的理由 上周我終於鼓起勇氣翻開了headfirst設計模式這本書,看看自己下一個設計模式要寫個啥,然後,我終於知道我為啥這麼久都沒寫設計模式了,headfirst的這個抽象工廠模式,額,我看了好幾次,都不太理解。 在我的印象中,簡單工廠,工廠方法,抽象工廠,這三個東西應該是層層遞進 ...


先編一個這麼久不寫的理由

上周我終於鼓起勇氣翻開了headfirst設計模式這本書,看看自己下一個設計模式要寫個啥,然後,我終於知道我為啥這麼久都沒寫設計模式了,headfirst的這個抽象工廠模式,額,我看了好幾次,都不太理解。

在我的印象中,簡單工廠,工廠方法,抽象工廠,這三個東西應該是層層遞進的,然後我帶著這個思路去看,emmmm,真的沒看懂,還好最近又補了一遍《大話設計模式》,揣著剛剛溫習了的新知識,然後又上了headfirst這條船,我感覺我這次應該是看懂了。

所以不寫並不是因為忙,也不是因為懶,什麼上分和吃雞,我根本都沒有聽說過,和一個996的碼農說這些,表示很扎心。

看完了工廠模式以後,發現後面居然是單例模式,兄弟,你是認真的嗎?(淚流滿面)

 

抽象工廠舉例分析

首先,我去看了一下這本書配套的源代碼,嗯,抽象工廠的類,感覺太多太多了,帶上測試類一共是35個,這要是貼出來,可以水很大的篇幅了,但是我是那種人嗎?當然不是!

碰到這種情況當然是直接給地址,有需要的小伙伴可以自己去看

github地址:https://github.com/bethrobson/Head-First-Design-Patterns

 

工廠模式遞進體系分析

先捋一捋書中描述的抽象工廠是個什麼意思,要說清楚這個,就必須先說一下它整個工廠模式的敘述的遞進關係,具體如下:

1,先拿一個賊簡單的創建Pizza的簡單工廠類PizzaStore,來忽悠我,這章很簡單,來學學吧,這章源代碼只有8個類

2,興衝衝的看完了第一部分,當然是一鼓作氣的看第二部分,第二部分總結起來大概是講這麼一個事情:

  1)我們公司在紐約的那個旗艦店(PizzaStore)生意不錯,現在我們要開一個分店,名字叫芝加哥披薩店(ChicagoPizzaStore)

  2)分店開了以後,原來的先進經驗不能丟啊,所以PizzaStore被抽象出來了,搞成了一個抽象類,作為基礎技術儲備

  3)PizzaStore變成了公司的基礎部門,專門指導披薩製作的流程

  4)旗艦店沒辦法啊,一方面自己的名字被占用了,一方面規範管理的風也吹來了,就改名字叫紐約披薩店(NYPizzaStore),旗下的所有披薩也順勢把名字也改了

  5)新開起來的芝加哥披薩店,倚靠了公司的技術實力,copy了一份旗艦店的菜單,但是也要照顧本地人的口味啊,所以就因地制宜,開發了自己的產品

  整個事情就是這樣,涉及到的類有:pizza抽象類(1個),PizzaStore抽象類(1個),實體披薩店(2個),實體店的各類Pizza(8個),測試類(1個)

  總的來說,這一部分的遞進關係很棒,也能很好的體現,簡單工廠和工廠方法最大的不同,工廠方法可以對工廠類做到:開閉原則

3,工廠方法扯完了,就來看一下,抽象工廠嘛,這一波操作我當時著實沒有看懂,就一直擱置了,為什麼當時沒看懂呢?

對於正常的抽象工廠的講解套路來說應該是這個樣子的:新增加一個產品線(抽象類,實現類),重新規劃一下Factory的介面和實現類(Factory里新加一個介面,實現類新增實現)

它沒有這樣搞,它直接根據披薩原料,重新抽象了一套抽象工廠模式的體系出來,不得不說,這個比剛纔說的那個新增產品線的例子漂亮太多了,原因有以下幾點:

  1)直接新增一個產品線的這種操作,雖然更容易理解,但是會給人一個誤導:我在原來Factory新增介面的這個操作,是符合設計模式,符合抽象工廠模式的

  2)抽象工廠的缺點是什麼?缺點就是,Factory的介面,在實際使用的時候,幾乎是無法新增產品的,修改太多了。所以它選擇了使用原料這套新體系來講解,更加符合實際

當然,它的例子也不是沒有缺點,個人感覺它的缺點有以下幾點:

  1)產生了類太多了,35個(上面有說過),對新手不友好,看到這個量級,稍微往後一翻又是一個單例模式,想著這個東西又不怎麼能用上,有求生欲望的,都不會在放棄的邊緣瘋狂試探……

  2)對它本身的工廠方法模式的體系(PizzaStore體系),也有很大量的修改,集中體現在各類Pizza實現類的縮減(取消了按店鋪名稱來創建的各類披薩,轉而使用了簡單工廠模式裡面的名字),Pizza的方法實現也有很多修改,主要是為了支持原料體系,當然,雖然產生了很多的修改,但是對外部提供的介面是沒有影響的,換句話說,雖然實現有了翻天覆地的變化,但是顧客還是無感知的,這個從測試代碼就能看出來:

工廠方法模式測試類:

public class PizzaTestDrive {
 
    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();
 
        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
        pizza = chicagoStore.orderPizza("cheese");
        System.out.println("Joel ordered a " + pizza.getName() + "\n");

        pizza = nyStore.orderPizza("clam");
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
        pizza = chicagoStore.orderPizza("clam");
        System.out.println("Joel ordered a " + pizza.getName() + "\n");

        pizza = nyStore.orderPizza("pepperoni");
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
        pizza = chicagoStore.orderPizza("pepperoni");
        System.out.println("Joel ordered a " + pizza.getName() + "\n");

        pizza = nyStore.orderPizza("veggie");
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
        pizza = chicagoStore.orderPizza("veggie");
        System.out.println("Joel ordered a " + pizza.getName() + "\n");
    }
}
View Code

抽象工廠測試類:

public class PizzaTestDrive {
 
    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();
 
        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println("Ethan ordered a " + pizza + "\n");
 
        pizza = chicagoStore.orderPizza("cheese");
        System.out.println("Joel ordered a " + pizza + "\n");

        pizza = nyStore.orderPizza("clam");
        System.out.println("Ethan ordered a " + pizza + "\n");
 
        pizza = chicagoStore.orderPizza("clam");
        System.out.println("Joel ordered a " + pizza + "\n");

        pizza = nyStore.orderPizza("pepperoni");
        System.out.println("Ethan ordered a " + pizza + "\n");
 
        pizza = chicagoStore.orderPizza("pepperoni");
        System.out.println("Joel ordered a " + pizza + "\n");

        pizza = nyStore.orderPizza("veggie");
        System.out.println("Ethan ordered a " + pizza + "\n");
 
        pizza = chicagoStore.orderPizza("veggie");
        System.out.println("Joel ordered a " + pizza + "\n");
    }
}
View Code

是一個非常贊的地方

 

抽象工廠實現過程

首先,我們從原料的工廠類入手:

public interface PizzaIngredientFactory {
 
    Dough createDough();
    Sauce createSauce();
    Cheese createCheese();
    Veggies[] createVeggies();
    Pepperoni createPepperoni();
    Clams createClam();
 
}

一共有6種原料可以創建,這個也是它源代碼類多的原因之一,由於大多數代碼都是相似的,所以這裡也就不一一的列舉了只列舉流程相關的實現

先看醬油(Sauce)相關的介面和實現:

public interface Sauce {
    String toString();
}

PlumTomatoSauce:

public class PlumTomatoSauce implements Sauce {
    public String toString() {
        return "Tomato sauce with plum tomatoes";
    }
}
View Code

MarinaraSauce:

public class MarinaraSauce implements Sauce {
    public String toString() {
        return "Marinara Sauce";
    }
}
View Code

然後看看麵團的相關介面和實現:

public interface Dough {
    String toString();
}

ThickCrustDough:

public class ThickCrustDough implements Dough {
    public String toString() {
        return "ThickCrust style extra thick crust dough";
    }
}
View Code

ThinCrustDough:

public class ThinCrustDough implements Dough {
    public String toString() {
        return "Thin Crust Dough";
    }
}
View Code

以上,是抽象工廠的基礎:產品族的工廠類,產品介面(或者是抽象類),產品的具體實現類請忽略它只是為了方便理解放到一起的

列舉了這個,再來看看具體的原料工廠:

ChicagoPizzaIngredientFactory:

public class ChicagoPizzaIngredientFactory 
    implements PizzaIngredientFactory 
{

    //我是麵團,我是這裡
    public Dough createDough() {
        return new ThickCrustDough();
    }

    //我是醬油,我在這裡
    public Sauce createSauce() {
        return new PlumTomatoSauce();
    }

    public Cheese createCheese() {
        return new MozzarellaCheese();
    }

    public Veggies[] createVeggies() {
        Veggies veggies[] = { new BlackOlives(), 
                              new Spinach(), 
                              new Eggplant() };
        return veggies;
    }

    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }

    public Clams createClam() {
        return new FrozenClams();
    }
}
View Code

NYPizzaIngredientFactory:

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {

    //我是麵團,我在這裡
    public Dough createDough() {
        return new ThinCrustDough();
    }

    //我是醬油,我在這裡
    public Sauce createSauce() {
        return new MarinaraSauce();
    }
 
    public Cheese createCheese() {
        return new ReggianoCheese();
    }
 
    public Veggies[] createVeggies() {
        Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
        return veggies;
    }
 
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }

    public Clams createClam() {
        return new FreshClams();
    }
}
View Code

實現也是很簡單的,這裡為了不占篇幅就隱藏了,有需要的自己點開看,接下來看一個這個原料體系的建立,需要修改的體現:

1,各類Pizza的實現類,需要持有PizzaIngredientFactory對象,並且prepare方法會有修改

public class ClamPizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;
 
    public ClamPizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }
 
    void prepare() {
        System.out.println("Preparing " + name);
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
        clam = ingredientFactory.createClam();
    }
}

2,具體的pizza工廠的修改:

public class ChicagoPizzaStore extends PizzaStore {

    protected Pizza createPizza(String item) {
        Pizza pizza = null;
        //創建各自的原料工廠
        PizzaIngredientFactory ingredientFactory =
        new ChicagoPizzaIngredientFactory();

        if (item.equals("cheese")) {
            //創建具體pizza的時候傳入原料工廠
            pizza = new CheesePizza(ingredientFactory);
            pizza.setName("Chicago Style Cheese Pizza");

        } else if (item.equals("veggie")) {

            pizza = new VeggiePizza(ingredientFactory);
            pizza.setName("Chicago Style Veggie Pizza");

        } else if (item.equals("clam")) {

            pizza = new ClamPizza(ingredientFactory);
            pizza.setName("Chicago Style Clam Pizza");

        } else if (item.equals("pepperoni")) {

            pizza = new PepperoniPizza(ingredientFactory);
            pizza.setName("Chicago Style Pepperoni Pizza");

        }
        return pizza;
    }
}

以上就是抽象工廠類的實現套路,以及在head first中,對原工廠方法模式具體影響的敘述,貼代碼是不可能貼代碼的,這輩子都不可能貼代碼的

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 就是這麼簡單的一句話,設置背景圖,並讓它100%的適應導航欄寬高,並設置不重覆,大小100%就OK了 轉載來源:https://blog.csdn.net/chenqk_123/article/details/44402355 ...
  • 在您的照片庫中加入成千上萬張高品質的照片,涵蓋各種主題和風格!下麵列出的網站提供可用於任何項目的圖像,沒有限制。您不必擔心因為一張小圖片的版權而導致麻煩。通過這些網站可以改善你的設計項目,這些是網上提供免費的高質量圖片最好的網站。作為設計師,都知道如何藉助免費素材來解放工作。他們經常會花好幾個小時去... ...
  • 一,效果圖。 二,代碼。 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>html 列表</title> </head> <body> <!--創建文本欄位text field--> <form action=""> first ...
  • 我們在做css的時候為了提高網站的效率減少伺服器請求,我們可以通過css來實現一些簡單的圖片特效,比如說三角形,這篇文章講解的是通過邊框實現不同的效果。 首先看一下不同邊框樣式的效果: 代碼部分: 1 <style type="text/css"> 2 .style-border b { 3 bor ...
  • jQuery CDN PS:jQuery 2.0 以上版本不再支持IE 6/7/8 REF: "https://www.sojson.com/jquery/down.html" 經多次測試,建議使用百度的CDN引用地址,官網的地址有可能造成部分地區打開略有延遲 Version Baidu Examp ...
  • Vue 組件之間傳值 一、父組件向子組件傳遞數據 在 Vue 中,可以使用 props 向子組件傳遞數據。 子組件部分: 這是 header.vue 的 HTML 部分,logo 是在 data 中定義的變數。 如果需要從父組件獲取 logo 的值,就需要使用 props: ['logo'] 在 p ...
  • ```php ...
  • 備忘錄,備份曾經發生過的歷史記錄,以防忘記,之後便可以輕鬆回溯過往。想必我們曾經都乾過很多蠢事導致糟糕的結果,當後悔莫及的時候已經是覆水難收了,只可惜這世界上沒有後悔藥,事後我們能做的只能去彌補過失,總結經驗。除非穿越時空,時光倒流,利用愛因斯坦狹義相對論,超越光速回到過去,破鏡重圓。 然而世界是殘 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...