設計模式之工廠模式(二)

来源:https://www.cnblogs.com/dimple91/archive/2019/04/20/10740506.html
-Advertisement-
Play Games

之前已經帶大家稍微入門了工廠模式(即簡單工廠模式)的方法,沒看過的朋友可以移步去查看一番。 "設計模式之工廠模式(一)" 。今天我們繼續吃著披薩,學習著工廠模式的接下來部分吧。 加盟披薩店 我們先前的披薩店已經經營有成,擊敗了部分競爭者,接下來的計劃就是開加盟店。作為經營者,你肯定希望確保加盟店運營 ...


之前已經帶大家稍微入門了工廠模式(即簡單工廠模式)的方法,沒看過的朋友可以移步去查看一番。設計模式之工廠模式(一)。今天我們繼續吃著披薩,學習著工廠模式的接下來部分吧。

加盟披薩店

我們先前的披薩店已經經營有成,擊敗了部分競爭者,接下來的計劃就是開加盟店。作為經營者,你肯定希望確保加盟店運營的質量,所以希望這些店都是用你那些經過時間考驗的代碼。

但是每個地方可能需要不同口味的披薩(比如A地區、B地區、C地區等),這就是開店地點以及該地區披薩美食家口味的影像。

如果利用先前的簡單工廠,那麼就是需要創建多個不同的工廠可以使用。代碼如下:

NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.orderPizza("Veggie");

ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaStore nyStore = new PizzaStore(chicagoFactory);
nyStore.orderPizza("Veggie");

給披薩店使用的框架

在使用簡單工廠的時候,我們發現加盟店的確是採用工廠創建披薩,但是其他部分,卻開始採用自己的方式。有個做法可以讓披薩製作活動局限於PizzaStore類,而同時又能讓這些加盟店可以自由地製作該地區的風味。

所要做的事情呢,就是把之前createPizza()方法放回到PizzaStore中,不過不是單純的放回來,而是把它設置成抽象方法,然後每個區域創建一個PizzaStore的子類即可。.放出部分代碼來看看

public abstract class PizzaStore {
    public Pizza orderPizza(String type) {
        // 現在createPizza()方法從工廠對象中移回PizzaStore
        Pizza pizza = createPizza(type);
        ...
        return pizza;
    }
    
    // 現在把工廠對象移動到這個方法中
    abstract Pizza createPizza(String type);
}

現在上面這個就作為超類;讓每個域類型都繼承這個PizzaStore,每個子類各自決定如何製造披薩。

允許子類做決定

我們現在要讓createPizza()能夠應對各種變化創建正確種類的披薩。做法是讓PizzaStore的各個子類負責定義自己的createPizza()方法。所以,我們會得到一些PizzaStore具體的子類,每個子類都有自己的披薩變體,而仍然適合PizzaStore框架,並使用調試好的orderPizza()方法。
;

這時候會有人肯定納悶了,PizzaStore的子類終究是子類,如何能做決定呢?關於這個方面,要從PizzaStore的orderPizza()方法觀點來看,此方法在抽象的PizzaStore內定義,但是只在子類中實現具體類型。

現在,更進一步地,orderPizza()方法對Pizza對象做了許多事情,但由於Pizza對象是抽象的,orderPizza()並不知道哪些實際的具體類參與進來了。換句話說,這就是解耦(decopule)。

當orderPizza()調用createPizza()時,就會由具體的披薩店來決定做哪一種披薩。那麼,子類是實時做出這樣的決定嗎?不是,但從orderPizza()角度來看,如果選擇在NYStylePizzaStore訂購披薩,就是由這個子類決定。

讓我們開一家披薩店吧

現在我們把加盟店開了。其實我們只需要繼承PizzaStore,然後提供createPizza()方法實現自己的披薩風味即可。比如紐約風味:

// NYPizzaStore擴展PizzaStore,所以擁有orderPizza方法(以及其他方法)
public class NYPizzaStore extends PizzaStore {

// createPizza()返回一個Pizza對象,由子類全權負責該實例化哪一個具體的Pizza
    Pizza createPizza(String item) {
        if (item.equals("cheese")) {
            return new NYStyleCheesePizza();
        } else if (item.equals("veggie")) {
            return new NYStyleVeggiePizza();
        } else if (item.equals("clam")) {
            return new NYStyleClamPizza();
        } else if (item.equals("pepperoni")) {
            return new NYStylePepperoniPizza();
        } else return null;
    }
}

工廠方法用來處理對象的創建,並將這樣的行為封裝在子類中。這樣,客戶程式中關於超類的代碼就和子類對象創建代碼解耦了。

如何利用披薩工廠方法訂購披薩

  1. 首先,需要取得披薩店的實例。A需要實例化一個ChicagoPizzaStore,而B需要一個NYPizzaStore
  2. 有了各自的PizzaStore,A和B分別調用orderPizza()方法,並傳入他們所喜愛的披薩類型
  3. orderPizza()調用createPizza()創建披薩。其中NYPizzaStore實例化的是紐約風味披薩,而ChicagoPizzaStore實例化的是芝加哥風味披薩。createPizza()會創建好的披薩當做返回值。
  4. orderPizza()並不知道真正創建的是哪一個披薩,只知道這是一個披薩,能夠被準備、被烘烤、被切片、被裝盒、然後提供給A和B

看看如何根據訂單生產這些披薩

  1. 先看看A的訂單,首先我們需要一個紐約披薩店:
// 建立一個NYPizzaStore的實例
PizzaStore nyPizzaStore = new NYPizzaStore();
  1. 現在有了一個店,可以下訂單了:
// 調用nyPizzaStore實例的orderPizza()方法
nyPizzaStore.orderPizza("cheese");
  1. orderPizza()方法於是調用cratePizza()方法
// 別忘了,工廠方法create-Pizza()是在子類中實現的。在這個例子中,他會返回紐約芝士披薩
Pizza pizza = createPizza("cheese");
  1. 最後,披薩必須經過下列的處理才算成功orderPizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();

吃披薩咯

我們需要先有一個比薩,不然披薩店開起來了,結果沒有產品,豈不是很尷尬。

// 從一個抽象披薩類開始,所有的具體披薩都必須派生自這個類
public abstract class Pizza {
    String name;
    String dough;
    String sauce;
    ArrayList<String> toppings = new ArrayList<String>();
 
    void prepare() {
        System.out.println("Prepare " + name);
        System.out.println("Tossing dough...");
        System.out.println("Adding sauce...");
        System.out.println("Adding toppings: ");
        for (String topping : toppings) {
            System.out.println("   " + topping);
        }
    }
  
    void bake() {
        System.out.println("Bake for 25 minutes at 350");
    }
 
    void cut() {
        System.out.println("Cut the pizza into diagonal slices");
    }
  
    void box() {
        System.out.println("Place pizza in official PizzaStore box");
    }
 
    public String getName() {
        return name;
    }

    public String toString() {
        StringBuffer display = new StringBuffer();
        display.append("---- " + name + " ----\n");
        display.append(dough + "\n");
        display.append(sauce + "\n");
        for (String topping : toppings) {
            display.append(topping + "\n");
        }
        return display.toString();
    }
}

我們來定義一些具體的子類,在這裡,其實就是定義紐約和芝加哥風味的芝士披薩

public class NYStyleCheesePizza extends Pizza {

    public NYStyleCheesePizza() { 
        name = "NY Style Sauce and Cheese Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";
 
        toppings.add("Grated Reggiano Cheese");
    }
}
public class ChicagoStyleClamPizza extends Pizza {
    public ChicagoStyleClamPizza() {
        name = "Chicago Style Clam Pizza";
        dough = "Extra Thick Crust Dough";
        sauce = "Plum Tomato Sauce";
 
        toppings.add("Shredded Mozzarella Cheese");
        toppings.add("Frozen Clams from Chesapeake Bay");
    }
 
    void cut() {
        System.out.println("Cutting the pizza into square slices");
    }
}

好了等久了吧,馬上來吃披薩了,這個時候剛好是下午4點左右,小編感覺已經餓的不行。

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");
    }
}

好了,至此我們已經開了紐約和芝加哥披薩店,並已經愉快的製作和吃上了披薩,而且這是通過我們的工廠方法模式創建並得到的。

關於認識工廠方法模式,因為這篇我們已經通過代碼來瞭解了下,我將在下一篇進行解釋併進一步認識這個模式,請大家敬請期待吧。

PS:因為工廠模式涉及的篇幅較大,幾篇文章可能存在不合理的銜接,小編會儘快輸出全部文章,讓大家能一次瞭解,在此給大家道個歉。

GitHub地址 HeadFirstDesign

愛生活,愛學習,愛感悟,愛挨踢


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

-Advertisement-
Play Games
更多相關文章
  • 在寫單文件組件時,一般都是把標簽、腳本、樣式寫到一起,這樣寫個人感覺有點不夠簡潔,所以就想著把樣式分離出去。 採用import載入樣式 在局部作用域(scoped)採用@import載入進來的樣式文件,想法是美好的。以為這樣載入進來的樣式文件也只對當前組件有效;可現實是殘酷的,這樣載入進來的樣式無法 ...
  • 一、路由機制(靜態資源文件處理) 1.1 Nodejs沒有根目錄 MIME類型:http://www.w3school.com.cn/media/media_mimeref.asp 在Apache中,它會自動將htdocs文件夾提供靜態化路由服務。 但是Nodejs沒有這個機制。 在文件夾中創建這樣 ...
  • 在單頁面應用程式(SPA)中,有些頁面的佈局結構是上下兩塊是固定,中間內容是變化的。這時在入口處固定上下部分就可以很好的解決這一問題。有少部分頁面沒有上下部分或不需要(如:用戶註冊、登陸頁面),針對這一情況怎麼解決 相容這兩種情況解決方案: App.vue 在入口處單個路由輸出 Frame.vue ...
  • 好處:方便了後端對HTTP請求中參數進行核驗,只需一次編寫效驗器,一行代碼便可對所有參數的pojo進行參數核驗!而且更改效驗邏輯時只需要更改效驗器類即可,實現瞭解耦合。 只需要程式員按照規範開發一個ParameterValidator類(如下圖1),將所有效驗方法寫在該類中即可在任意地方使用一行代碼 ...
  • 原型模式概述 定義:使用原型實例指定待創建對象的類型,並且通過複製這個原型來創建新的對象。簡單的來說就是克隆(Clone),通過已經存在的,將其複製而產生新的。原型模式屬於創建型模式,將一個原型對象傳給要發動創建的對象(客戶端對象),該對象通過請求原型對象複製自己來實現創建過程。 既然是通過Clon ...
  • Spring Boot 2.0 升級指南 前言 Spring Boot已經發佈2.0有5個月多,多了很多新特性,一些坑也慢慢被填上,最近有空,就把項目中Spring Boot 版本做了升級,順便整理下升級的時候遇到的一些坑,做個記錄。後續的教程就以最新的2.03版本為主。參考官方文檔翻譯 在你開始之 ...
  • 本文將從簡單的場景引入, 逐步優化, 最後給出具體的責任鏈設計模式實現. 場景引入 首先我們考慮這樣一個場景: 論壇上用戶要發帖子, 但是用戶的想法是豐富多變的, 他們可能正常地發帖, 可能會在網頁中淺入html代碼, 可能會使用錯誤的表情格式, 也可能發送一些敏感信息. 作為論壇的管理員必須對用戶 ...
  • SSO 單點登錄:一次登錄,處處登錄。 只需在一個登錄認證服務下進行登錄後,就可訪問所有相互信任的應用 同域 SSO 1. session-cookie機制:服務端通過cookie認證客戶端。 用戶第一次登錄後,服務端將返回一個cookie給客戶端(這個cookie包含session id),用戶下 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...