工廠模式講解, 引入Spring IOC

来源:https://www.cnblogs.com/tanshaoshenghao/archive/2019/04/25/10770659.html
-Advertisement-
Play Games

[TOC] 引入 假設有一個司機, 需要到某個城市, 於是我們給他一輛汽車 如果我們希望給到這個司機的始終是一輛車, 應該怎麼做? (單例) 首先我們不能讓司機自己通過 產生一輛汽車, 而是應該通過調用 類中的某個方法對外提供車.   簡單工廠 下麵考慮, 如果我們不希望只有汽車這種交通工 ...


目錄

引入

  • 假設有一個司機, 需要到某個城市, 於是我們給他一輛汽車
public class Demo {
    public static void main(String[] args) {
        Car car = new Car();
        car.run();
    }
}

public class Car {
    public void run(){
        System.out.println("汽車正在向前跑...");
    }
}
  • 如果我們希望給到這個司機的始終是一輛車, 應該怎麼做? (單例)
  • 首先我們不能讓司機自己通過new產生一輛汽車, 而是應該通過調用Car類中的某個方法對外提供車.
public class Car {
    private static Car car = new Car();//用於提供給外界, 始終是同一輛車

    private Car(){};//私有構造方法, 在類之外不能通過new獲得本類對象了, 保證了單例

    public Car getInstance(){
        return car;
    }

    public void run(){
        System.out.println("汽車正在向前跑...");
    }
}

public static void main(String[] args) {
    Car car = Car.getInstance();
    car.run();
}

 

簡單工廠

  • 下麵考慮, 如果我們不希望只有汽車這種交通工具, 我們希望可以定製交通工具, 並定製生產交通工具的流程, 應該怎麼做?
  • 一旦產生由汽車到交通工具這樣的概念, 就應該想到多態. 我們可以定義一個Moveable介面, 在介面中聲明run()方法, 所有的交通工具類都實現該介面.
  • 對於定製生產流程, 我們可以通過一個工廠進行生產對應的交通工具.
public interface Moveable {
    void run();
}

public class Car implements Moveable{

    public Car(){};//私有構造方法, 在類之外不能通過new獲得本類對象了, 保證了單例

    public void run(){
        System.out.println("汽車正在向前跑...");
    }
}

public abstract class VehicleFactory {
    public abstract Moveable create();
}

public class CarFactory extends VehicleFactory {
    @Override
    public Moveable create() {
        return new Car();
    }
}

//Test
public static void main(String[] args) {
    VehicleFactory factory = new CarFactory();
    Moveable m = factory.create();
    m.run();
}

 

抽象工廠

  • 下麵把簡單工廠的畫面從腦海中清空, 講述另一種工廠實現.
  • 我們假設開頭的司機不是一個普通的司機, 他除了需要一種交通工具以到達某個城市外, 他還需要一把AK47, 並且還需要一個蘋果以備路上不時之需.
  • 所以我們需要給他一個工廠來製造這一系列產品.
  • 為了提高可擴展性, 我們還希望不同的工廠可以製作不同系列的產品, 比如上面說的A工廠製造的是汽車, AK47, 蘋果; 而B工廠製造的是飛機, 火箭炮, 旺仔小饅頭.
//test
public static void main(String[] args) {
    AbstractFactory factory = new Factory1();
    Vehiche v = factory.createVehiche();
    Weapon w = factory.createWeapon();
    Food f = factory.createFood();

    v.run();
    w.fire();
    f.eat();
}

public abstract class Vehiche {//交通工具的抽象類
    public abstract void run();
}

public abstract class Weapon {//武器的抽象類
    public abstract void fire();
}

public abstract class Food {//食物的抽象類
    public abstract void eat();
}

public class Car extends Vehiche{一種具體的交通工具
    @Override
    public void run() {
        System.out.println("小汽車啟動...");
    }
}

public class AK47 extends Weapon {//一種具體的武器
    @Override
    public void fire() {
        System.out.println("噠噠噠...");
    }
}

public class Apple extends Food{//一種具體的食物
    @Override
    public void eat() {
        System.out.println("大口吃蘋果...");
    }
}

//抽象工廠
public abstract class AbstractFactory {
    public abstract Vehiche createVehiche();
    public abstract Weapon createWeapon();
    public abstract Food createFood();
}

//抽象工廠的實現1
public class Factory1 extends AbstractFactory {
    @Override
    public Vehiche createVehiche() {
        return new Car();
    }

    @Override
    public Weapon createWeapon() {
        return new AK47();
    }

    @Override
    public Food createFood() {
        return new Apple();
    }
}

 

  • 總結一下, 抽象工廠和簡單工廠各有什麼優劣?
  • 抽象工廠能夠生產一系列產品, 也能方便地替換掉一系列產品, 但是如果想要在產品系列中添加多一個品種將會非常麻煩. 比如說在上面的系列產品中添加一個盔甲抽象類, 那麼抽象工廠以及對應的實現都要修改源碼了.
  • 而簡單工廠能夠靈活的生產但一個品種的產品, 但是如果生產的品種較多, 會出現工廠泛濫的問題.
  • 兩者優劣互補, 那麼有沒有可以相容兩者優點的工廠實現呢? 下麵看spring的工廠實現, 它給出了一種解決方案.

 

Spring的bean工廠

  • 我們再次考慮最原始的情況, 有一個Moveable介面, 裡面有run方法, Car小汽車類實現了該介面.
public static void main(String[] args) {
    Moveable m = new Car();
    m.run();
}

public interface Moveable {
    void run();
}

public class Car implements Moveable{
    @Override
    public void run() {
        System.out.println("小汽車往前跑...");
    }
}
  • 在Spring的bean工廠中, 新對象不是通過new關鍵字獲取的, 而是通過配置文件獲取的.
  • 具體的過程是: 先讀取配置文件獲得該類的class對象, 然後通過class對象創建具體的實例對象.
public static void main(String[] args) throws Exception {
    //獲取配置文件
    Properties props = new Properties();
    props.load(Test.class.getClassLoader().getResourceAsStream("spring.properties"));
    //獲取配置文件中配置的類
    String vehicheTypeName = props.getProperty("vehicheTypeName");
    //反射生成對應的對象
    Moveable m = (Moveable) Class.forName(vehicheTypeName).newInstance();
    m.run();
}

//spring.properties
vehicheTypeName=designPattern.factory.springFactory.Car

 

  • 上面是對spring中bean工廠使用的模擬, 下麵我們使用真實的spring來生成Car對象, 對比一下.
public static void main(String[] args) throws Exception {
    BeanFactory bf = new ClassPathXmlApplicationContext("applicationContext.xml");
    Vehiche v = (Vehiche)bf.getBean("v");
    v.run();
}

//配置文件
<bean id="v" class="designPattern.factory.Car">
</bean>
  • 經過對比我們發現我們自己寫的簡單工廠和spring的bean工廠在使用上沒有什麼區別, 確實spring使用起來就是這麼簡單, 下麵我們模擬一下spring的bean工廠實現.

 

模擬Spring工廠實現

模擬IOC

  • 都說spring是個bean容器, 以下的代碼將展示它是如何生成bean, 並把bean放入容器中供用戶獲取的.
  • 思路比較簡單:
  1. 創建BeanFactory工廠介面, 添加方法getBean().
  2. 創建BeanFactory的實現類ClassPathXmlApplicationContext. 將在該實現類中展示IOC的具體實現.
  3. ClassPathXmlApplicationContext需要一個container容器存放創建的bean對象, 這裡使用HashMap實現.
  4. ClassPathXmlApplicationContext的構造方法中讀取spring的配置文件, 這裡使用到了dom4j. 讀取配置文件後根據beanclass屬性使用反射創建出bean對象. 然後把idbean對象分別作為keyvalue添加到容器中.
  5. 當工廠被調用getBean()方法時, 從容器中找到對應的bean並返回.
public static void main(String[] args) throws Exception {
    BeanFactory bf = new ClassPathXmlApplicationContext("applicationContext.xml");
    Vehiche v = (Vehiche) bf.getBean("v");
    v.run();
}

//BeanFactory的實現類
public class ClassPathXmlApplicationContext implements BeanFactory {

    private Map<String, Object> container = new HashMap<>();//用於存放bean對象的容器
    
    //在構造方法中讀取xml配置文件, 把bean對象都創建好並放入容器中
    public ClassPathXmlApplicationContext(String propAddr) throws Exception {
        SAXReader reader = new SAXReader();
        File file = new File(this.getClass().getClassLoader().getResource(propAddr).toURI());
        Document document = reader.read(file);
        Element root = document.getRootElement();
        List<Element> childElements = root.elements();

        for (Element child : childElements) {
            Object bean = Class.forName(child.attributeValue("class")).newInstance();
            container.put(child.attributeValue("id"), bean);
        }
    }

    @Override
    public Object getBean(String beanId) {
        return container.containsKey(beanId) ? container.get(beanId) : null;
    }
}

//極簡BeanFactory
public interface BeanFactory {
    Object getBean(String beanId);
}

//xml中配置的bean
<bean id="v" class="designPattern.factory.Car">
</bean>

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

-Advertisement-
Play Games
更多相關文章
  • 目錄: 註意:本地json文件和json文件里的圖片地址都必須寫在static 靜態文件夾里;否則json文件里的url地址找不到。 major_info.json文件里的圖片路徑寫法 頁面通過v-bind的方式載入: ...
  • 1.第一步,這是目錄結構 2.接下來是build/webpack.dev.conf.js文件需要配置的內容 代碼: //vue配置請求本地json數據const express = require('express')const app = express()const appData = requ ...
  • 有一次我們說到擲骰子那個游戲,當時是用了一個steps屬性+雪碧圖來製作幀動畫,這當然頗為不錯,但其實一開始我想的不是這樣的,我想的是用真的3d和動畫去做,這個方案涉及到不少空間的知識,今天來給大伙好好說說,這css 3d到底怎麼玩。 先上效果圖: 基本思路:三層結構:視角容器>>載體>>具體3d圖 ...
  • 構造函數-->原型 >prototype-->__proto__-->constructor-->原型鏈 構造函數 什麼是構造函數?我理解構造函數就是可以用來生成實例的函數。 上面的代碼,f是函數Func new出來的實例,f是函數Func的實例,所以Func被稱為構造函數。那麼 Func的構造函數 ...
  • 本章主要介紹與Vue.js有關的一些概念與技術,並幫助你瞭解它們背後相關的工作原理。通過對本章的學習,即使從未接觸過Vue.js,你也可以運用這些知識點快速構建出一個Vue.js應用。1.1 Vue.js 是什麼Vue.js的官方文檔中是這樣介紹它的:簡單小巧的、漸進式JavaScript框架,是以... ...
  • 在創建store時, ,除了reducer函數,初始狀態,還可以傳入 。這個enhancer在createStore的源碼中是這樣使用的 它可以接受createStore方法併進行 自定義改裝 ,然後再使用改裝後的方法創建倉庫。 而redux官方提供的enhancer就只有 。 applyMiddl ...
  • 實現物體的旋轉、跳動以及場景陰影的開啟與優化,本程式將創建一個場景,並實現物體的動畫效果 ...
  • 定義: 定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使他們能夠自動更新自己。 結構:(書中圖,侵刪) 一個抽象的觀察者介面,擁有一個更新方法 若幹個具體的觀察者類 一個抽象的subject類,包含一個抽象觀察者的集合,並擁有 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...