設計模式詳解之創建型模式——單例、原型和工廠

来源:https://www.cnblogs.com/damocleses/archive/2022/04/26/16194630.html
-Advertisement-
Play Games

前言 本文主要介紹ArcGis的ArcEngine開發,學習時,我們需要放下心裡障礙,那就是Gis開發只是普通的軟體開發,並不需要專業的GIS知識,就是非常普通的,調用相關的C++開發的COM組件。 開發環境:VS2017。 ArcEngine版本:10.1。 基礎學習 正式使用ArcGis之前,需 ...


目錄
前言:相信作為程式開發,或多或少都接觸甚至使用過設計模式,甚至對於有些設計模式的概念都已經很熟悉了,但是在實際開發項目的時候是否有使用過這些模式呢,可能比較少甚至沒有。有些設計模式確實在架構中更實用一些,這也是部分原因。但不管怎樣,最起碼常用的幾種設計模式還是需要瞭解的,本文介紹幾種常見的設計模式,希望讀者能從中有所收穫並學以致用。

什麼是設計模式

設計模式(Design Pattern)是前輩們對代碼開發經驗的總結,是解決特定問題的一系列套路。它不是語法規定,而是一套用來提高代碼 可復用性 、 可維護性 、可讀性、穩健性以及安全性的解決方案。

假設有一個空房間,我們要日復一日地往裡 面放一些東西。最簡單的辦法當然是把這些東西 直接扔進去,但是時間久了,就會發現很難從這 個房子里找到自己想要的東西,要調整某幾樣東 西的位置也不容易。所以在房間里做一些柜子也 許是個更好的選擇,雖然柜子會增加我們的成 本,但它可以在維護階段為我們帶來好處。使用 這些柜子存放東西的規則,或許就是一種模式。

在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名為 Design Patterns - Elements of Reusable Object-Oriented Software(中文譯名:設計模式 - 可復用的面向對象軟體元素) 的書,該書首次提到了軟體開發中設計模式的概念。
四位作者合稱 GOF(四人幫,全拼 Gang of Four)。他們所提出的設計模式主要是基於以下的面向對象設計原則。

  • 對介面編程而不是對實現編程——依賴倒置原則
  • 優先使用對象組合而不是繼承——合成復用原則。

設計模式的六大原則:

  1. 單一職責原則(Single Responsibility Principle)。職責清晰
  2. 里氏替換原則(Liskov Substitution Principle)——任何使用基類的地方,都可以透明的使用其子類
  3. 迪米特法則 (Law Of Demeter)—— 一個對象應該對其他對象保持最少的瞭解,即高聚合低耦合
  4. 依賴倒置原則(Dependence Inversion Principle)—— 依賴抽象,而不是依賴細節
  5. 介面隔離原則(Interface Segregation Principle)—— 客戶端不應該依賴它不需要的介面; 一個類對另一個類的依賴應該建立在最小的介面上;
  6. 開閉原則 (Open Closed Principle) —— 對擴展開發,對修改關閉

創建型模式

單例模式(Singleton Pattern)

單例模式想必大家都已經耳熟能詳了,這是很常見的一種設計模式,也是最簡單的一種設計模式。它提供了一種創建對象的最佳方式。這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。

使用單例模式的實例:.NET Core依賴註入生命周期中的Singleton。生命周期為Singleton的服務全局唯一,每次調用都是調用的同一個服務.

單例模式有兩種寫法,懶漢式餓漢式

懶漢式,顧名思義,比較懶,一開始的時候不會創建對象,要用到了才會想起來去創建對象,寫法如下:

//懶漢式單例寫法
public class SingletonPattern
{
    private static SingletonPattern _singletonInstance;
    //構造函數私有化是關鍵
    private SingletonPattern()
    {
 
    }
    //雙重檢驗,提升性能
    public static SingletonPattern GetInstance(bool useLock = true)
        {
         if (_singletonInstance is null)
            {
                lock (singleton_lock)
                {
                    if (_singletonInstance is null)
                    {
                        _singletonInstance = new SingletonPattern();
                    }
                }
            }
            return _singletonInstance;
       }
}

註意:懶漢式要考慮多線程安全問題,這裡使用雙重檢驗鎖以確保線程安全並提升性能

餓漢式比較簡單,只需要做個是否已經創建的判斷即可,寫法如下:

//餓漢式單例寫法
public class SingletonPattern
{
    private static SingletonPattern _singletonInstance = new SingletonPattern();
    //構造函數私有化是關鍵
    private SingletonPattern()
    {
 
    }
 
    public static SingletonPattern GetInstance(bool useLock = true)
        {          
            if (_singletonInstance is null)
            {
                _singletonInstance = new SingletonPattern();
            }            
            return _singletonInstance;
       }
}

該模式的適用場景:

  • 一個全局類,頻繁創建和銷毀,如服務類、工具類等
  • 需要控制實例數量,節約系統資源的時候

原型模式(Prototype Pattern)

原型模式(Prototype Pattern)是用於創建重覆的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。這種模式是實現了一個原型介面,該介面用於創建當前對象的克隆。當直接創建對象的代價比較大時,則採用這種模式。例如,一個對象需要在一個高代價的資料庫操作之後被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新資料庫,以此來減少資料庫調用。

原型模式的難點在於對對象的克隆,如果對象比較複雜,嵌套的屬性對象比較多,自己實現克隆方法會比較麻煩。不過還好目前主流語言都支持深度克隆對象,如Java的Serializable, javascript的cloneDeep等等,深度克隆也可以通過序列化和反序列化來實現,因此支持序列化和反序列化的語言都可以用這種方式實現深度克隆。

原型的實現方式與單例相差不大,重點是返回的實例是克隆對象

public class PrototypePattern
   {
       private static PrototypePattern _protetypeInstance = new PrototypePattern();
 
       private PrototypePattern()
       {
 
       }
 
       public static PrototypePattern GetInstance()
       {
           //重點在於返回克隆的對象
           PrototypePattern clone = GetDeepCloneObj();
           return clone;
       }
   }

原型適用場景:

  • 對象創建複雜,消耗資源大,又需要重覆創建類似對象

工廠模式(Factory Pattern)

工廠模式在GOF的設計模式中分為工廠方法和抽象工廠,實際上簡單工廠、工廠方法和抽象工廠的分類更為普遍一些。

簡單工廠模式(Sinple Factory Pattern)

簡單工廠主要隔離了使用者和產品,使用者需要使用產品時,直接向工廠請求,而不用知道具體產品的實現。也就是前面說的依賴倒置原則!

簡單工廠

上圖可以看出,用戶要創建一個產品,不需要知道產品的具體實現,只需要知道創建產品的工廠即可。實現代碼如下:

public class SimpleFactoryPattern
    {
        public static IRunner CreateRunner(PatternEnum pattern)
        {
            switch (pattern)
            {
                case PatternEnum.Singleton:
                    return new SingletonRunner();
                case PatternEnum.Prototype:
                    return new PrototypeRunner();
                case PatternEnum.Factory_Method:
                    return new FactoryMethodRunner();
                case PatternEnum.Abstract_Factory:
                    return new AbstractFactoryRunner();
                default:
                    return null;
            }
        }
    }
 
//使用
class Program
  {
        static void Main(string[] args)
        {
            var pattern = PatternEnum.Abstract_Factory;
            var prototype = SimpleFactoryPattern.CreateRunner(pattern);
            prototype.Run();
 
            Console.ReadKey();
        }
  }

簡單工廠適用於比較簡單的情況下,可以屏蔽對象創建細節,對於對象來說符合開閉原則。但是對於工廠來說並不符合開閉原則,因為當需要新增一個對象時,需要修改工廠的內容。另外當需要生成的對象過多時或者經常需要修改時,該模式就顯得不夠用了。

這時候就需要使用工廠方法了

工廠方法模式(Factory Method Pattern)

工廠方法

如上圖,工廠方法相比於簡單工廠,改變在於將具體的工廠也屏蔽了,用戶不需要知道需要用什麼工廠,只要調用抽象工廠的方法即可。實現如下:

 //抽象工廠
 public abstract class MounseFactoryMethod
   {
       public abstract IMouse CreateMouse();
   }
   //具體實現工廠
   public class DellMouseFactory : MounseFactoryMethod
   {
       public override IMouse CreateMouse()
       {
           return new DellMouse();
       }
   }
   //具體實現工廠
   public class HpMouseFactory : MounseFactoryMethod
   {
       public override IMouse CreateMouse()
       {
           return new HpMouse();
       }
   }
   //當具體的實現工廠太多時,可以結合簡單工廠,利用簡單工廠創建具體工廠
   //也可以利用反射創建工廠
   public class MouseFactory
   {
       public static MounseFactoryMethod CreateMouseFactory(BrandEnum brand)
       {
           switch (brand)
           {
               case BrandEnum.Dell:
                   return new DellMouseFactory();
               case BrandEnum.Hp:
                   return new HpMouseFactory();
               default:
                   return null;
           }
       }
   }
 
   //使用
   class Program
 {
       static void Main(string[] args)
       {                       
           var mouseFactory = MouseFactory.CreateMouseFactory(BrandEnum.Dell);
           var mouse = mouseFactory.CreateMouse();
           mouse.Click();        
       }
 }

工廠方法的工廠是符合開閉原則的,當有新產品時,只需要添加新的工廠即可,不需要改動已有工廠代碼。
在上面的代碼中,我們抽象了一個滑鼠生產工廠MounseFactoryMethod,兩個具體生產工廠DellMouseFactory 和HpMouseFactory 。另外還額外使用了一個簡單工廠MouseFactory來選擇要使用的具體工廠。

工廠方法在實際使用中比較常見,當需要創建的對象種類比較多且新增或刪除比較頻繁時,工廠方法是不錯的選擇。

抽象工廠(Abstract Factory)

首先瞭解下產品族的概念:

產品族

如上圖,擁有相同特性的產品稱為一個產品等級,同一產品平臺的不同產品稱為一個產品族

抽象工廠用於處理比較複雜的產品。舉個例子,上述工廠方法中的MounseFactoryMethod專門用於生產滑鼠,而DellMouseFactory 和HpMouseFactory 則分別用於生產戴爾滑鼠和惠普滑鼠,它們都是一個產品等級的產品。當我們不僅需要生產滑鼠,還要生成鍵盤時,光是一個MounseFactoryMethod已經不能滿足生產需要,這時候工廠生產的就不只是單一產品,而是一個產品族。類圖如下:

抽象工廠

上圖中,我們定義了一個ComputerFactory,這個工廠能夠生產更豐富的產品(滑鼠和鍵盤),戴爾和惠普分別有獨立的工廠生產自己的滑鼠和鍵盤。

像上圖這種,生產產品族的工廠稱為抽象工廠。

抽象工廠和工廠方法的區別在於,工廠方法只能生產單一產品,也就是產品介面只有一個,而抽象工廠的產品可能是來自不同的介面。
實現代碼如下:

public abstract class ComputerAbstractFactory
  {
      public abstract IMouse CreateMouse();
      public abstract IKeyboard CreateKeyboard();
  }
 
  public class DellAbstractFactory : ComputerAbstractFactory
  {
      public override IKeyboard CreateKeyboard()
      {
          return new DellKeyboard();
      }
 
      public override IMouse CreateMouse()
      {
          return new DellMouse();
      }
  }
 
  public class HpAbstractFactory : ComputerAbstractFactory
  {
      public override IKeyboard CreateKeyboard()
      {
          return new HpKeyboard();
      }
 
      public override IMouse CreateMouse()
      {
          return new HpMouse();
      }
  }
 
  //使用
  class Program
{
      static void Main(string[] args)
      {                                 
          Console.WriteLine("使用抽象工廠 DellAbstractFactory 創建產品");
          var dellFactory = new DellAbstractFactory();
          dellFactory.CreateKeyboard().Click();
          dellFactory.CreateMouse().Click();
 
          Console.WriteLine("使用抽象工廠 HpAbstractFactory 創建產品");
          var hpFactory = new HpAbstractFactory();
          hpFactory.CreateKeyboard().Click();
          hpFactory.CreateMouse().Click();            
      }
}

工廠模式總結

  1. 簡單工廠不符合開閉原則,僅使用於產品種類少、修改不頻繁的情況
  2. 工廠方法符合開閉原則,但只適用單一產品等級的情況
  3. 抽象工廠符合開閉原則,適用於生產產品族的情況

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

-Advertisement-
Play Games
更多相關文章
  • 作者:等不到的口琴 鏈接:www.cnblogs.com/Courage129/p/14383897.html 今天總結一下負載均衡中LVS與Nginx的區別,好幾篇博文一開始就說LVS是單向的,Nginx是雙向的,我個人認為這是不准確的,LVS三種模式中,雖然DR模式以及TUN模式只有請求的報文經 ...
  • 函數式編程 高階函數 Python學習交流Q群:906715085#### map(func, *iterable) def fn(x, y, z): pass map(fn, range(10), range(10), range(10)) Python學習交流Q群:906715085### fi ...
  • 今天給大家分享的是第四篇,前面幾篇我都放在上面了。小伙伴可以自己領取鞏固複習。 方法重寫 Python學習交流Q群:906715085#### print(" 方法重寫 "); # coding=utf-8 class Parent: # 定義父類 def myMethod(self): print ...
  • dependencyManagement與dependencies區別 dependencyManagement里只是聲明依賴,並不實現引入,因此子項目需要顯式的聲明需要用的依賴。如果不在子項目中聲明依賴,是不會從父項目中繼承下來的; 生效方式: 只有在子項目中寫了該依賴項,並且沒有指定具體版本,才 ...
  • 目錄結構 . ├── protos │ └── hello_world.proto ├── dists │ ├── __init__.py │ ├── hello_world_pb2.py │ └── hello_world_pb2_grpc.py ├── server.py ├── client. ...
  • 優化的基於樹狀位壓縮數組的字元集合,在提供高性能的同時並不需要消耗過多記憶體,可以完全取代任何用到 HashSet ...
  • 原文鏈接:https://www.cnblogs.com/ysmc/p/16197223.html BootstrapBlazor 官網地址:https://www.blazor.zone Bootstrap Blazor 提供了一個快速創建項目的項目模板,安裝方法可參考我這一篇文章:Bootstr ...
  • 聽說Docker這玩意挺長時間了,新建Web應用的時候,也註意到有個啟用Docker的選項。 前兩天掃了一眼《【大話雲原生】煮餃子與docker、kubernetes之間的關係》,覺得有點意思,決定試試Docker。 然後被坑、百度... 現將整個過程記錄一下 一、新建項目 點擊創建,被通知“需要安 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...