前言 本文主要介紹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)。他們所提出的設計模式主要是基於以下的面向對象設計原則。
- 對介面編程而不是對實現編程——依賴倒置原則
- 優先使用對象組合而不是繼承——合成復用原則。
設計模式的六大原則:
- 單一職責原則(Single Responsibility Principle)。職責清晰
- 里氏替換原則(Liskov Substitution Principle)——任何使用基類的地方,都可以透明的使用其子類
- 迪米特法則 (Law Of Demeter)—— 一個對象應該對其他對象保持最少的瞭解,即高聚合低耦合
- 依賴倒置原則(Dependence Inversion Principle)—— 依賴抽象,而不是依賴細節
- 介面隔離原則(Interface Segregation Principle)—— 客戶端不應該依賴它不需要的介面; 一個類對另一個類的依賴應該建立在最小的介面上;
- 開閉原則 (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();
}
}
工廠模式總結
- 簡單工廠不符合開閉原則,僅使用於產品種類少、修改不頻繁的情況
- 工廠方法符合開閉原則,但只適用單一產品等級的情況
- 抽象工廠符合開閉原則,適用於生產產品族的情況